Creating a blog with Flask - python

I'm learning flask and I have a little problem.
I made an index template, where are blog post titles.
{% for title in titles %}
<!-- Main Content -->
<div class="container">
<div class="row">
<div class="col-lg-8 col-lg-offset-2 col-md-10 col-md-offset-1">
<div class="post-preview">
<a href="{{ url_for('post')}}">
<h2 class="post-title">
{{ title[0] }}
</h2>
</a>
<p class="post-meta">Posted by {{ author }}</p>
</div>
</div>
</div>
</div>
{% endfor %}
Here is part of my post.html template.
<div class="container">
<div class="row">
<div class="col-lg-8 col-lg-offset-2 col-md-10 col-md-offset-1">
<p>{{ post_text1 | safe }}</p>
<hr>
<div class="alert alert-info" role="alert">Posted by
{{ author }}
</div>
</div>
</div>
</div>
I'm using sqlite3. Currently every title leads to same post.html where is first text from first post.
How to make every title direct to their post text? I mean, if I click on the first title it should bring up post.html and there should be first text. Second title should show second text.
Should I write program, that creates new html for every post or is there any other way?
#app.route('/')
def index():
db = connect_db()
titles = db.execute('select title from entries')
titles = titles.fetchall()
author = db.execute('select author from entries order by id desc')
author = author.fetchone()
return render_template('index.html', titles=titles[:], author=author[0])
#app.route('/post/')
def post():
db = connect_db()
post_text1 = db.execute('select post_text from entries')
post_text1 = post_text1.fetchone()
author = db.execute('select author from entries where id=2')
author = author.fetchone()
return render_template('post.html', post_text1=post_text1[0], author=author[0])

The problem comes from here <a href="{{ url_for('post')}}">.
What this tells Flask is to make a url for post, which is something you have defined in views as def post(argument) but you are not providing an argument. So if for example you are making you are taking your posts based on id, your view would would ask for /<int:post_id>/ in url and post_id would be passed as an argument based on which you would find the specific post and pass that to the template.
Your url_for should reflect this, you should have {{ url_for('post', post_id=title[1]) }} or wherever you are storing your equivalent of post_id (maybe that's title for you).
Edit:
Baed on your edit, your problem is that you are not telling Flask which post to fetch. You need either ID, or slug, or something that will go in the url and will tell you which post you are looking for. Your function right now is static and is always fetching the first entry in your database. The changes required are:
#app.route('/')
def index():
db = connect_db()
titles = db.execute('select title, id from entries')
titles = titles.fetchall()
author = db.execute('select author from entries order by id desc')
author = author.fetchone()
return render_template('index.html', titles=titles, author=author[0])
#app.route('/post/<int:post_id>/')
def post(post_id):
db = connect_db()
post_text = db.execute('select post_text from entries where id = ?', post_id)
post_text = post_text1.fetchone()
author = db.execute('select author from entries where id=2')
author = author.fetchone()
return render_template('post.html', post_text1=post_text, author=author)
<a href="{{ url_for('post', post_id=title[1])}}">
Also your author fetching is weird, you should have them stored (at least their ids) next to entries. Then I'd recomend some naming changes etc. It's hard to just answer the question and not write the code for you, as this is a site for answering specific questions, not writing code on demand :) Try to understand what I wrote here, play around with it a bit more etc. to fully undnerstand.
tl;dr: Posts have to get an argument and then fetch a post identified by that argument, the program can't magically tell which post you clicked on.

Related

Display counter of likes per post with Django is not working

I want to get a variable on the views.py file that retrieves the list of likes for each post. So, then on the HTML file, I would use .count so I can get the number of items on the list and finally be displayed on the DOM.
I first made classes on models.py. There, I have 3 classes: User, Post, and Like. User is from the default User class from Django. Post is a class that gets information about the post like the author, description, and timestamp. And on the Like class, I get the user and post.
from django.contrib.auth.models import AbstractUser
from django.db import models
class User(AbstractUser):
pass
class Post(models.Model):
author = models.ForeignKey("User", on_delete=models.CASCADE, related_name="user")
description = models.CharField(max_length=1000)
timestamp = models.DateTimeField(auto_now_add=True)
def __str__(self):
return f"{self.id}: {self.author}"
class Like (models.Model):
user = models.ForeignKey("User", on_delete=models.CASCADE, default='', related_name="user_like")
post = models.ForeignKey("Post", on_delete=models.CASCADE, default='', related_name="post_like")
def __str__(self):
return f"{self.id}:{self.user} likes {self.post}"
Second, I made a function on views.py called "index". There, I get the whole list of posts (on the posts variable), then I tried to create the variable (totalLikesSinglePost), which should get the list of likes for each post.
def index(request):
posts = Post.objects.all().order_by("id").reverse()
# Pagination Feature (OMIT THIS, IF YOU WANT)
p = Paginator(posts, 10)
pageNumber = request.GET.get('page')
postsPage = p.get_page(pageNumber)
# Total likes of each post. DOESN'T WORK ⏬
for postSingle in posts:
totalLikesSinglePost = Like.objects.all().filter(post = postSingle)
return render(request, "network/index.html", {
"posts": posts,
"postsPage": postsPage,
"totalLikesPost": totalLikesSinglePost
})
Finally, on the HTML file, there I get each post with its information and the number of likes. However, the output just displays the number 0
{% for post in postsPage %}
<div class="row">
<div class="col">
<div class="card h-100" id="post-grid">
<div class="card-body">
<h5>{{ post.author }}</h5>
<div> | {{ post.timestamp }}</div>
<p>{{ post.description }}</p>
<div class="card-buttonsContainer">
<strong style="color: red">{{ totalLikesPost.count }}</strong> <!--0 IS DISPLAYED-->
</div>
</div>
</div>
</div>
</div>
{% endfor %}
And of course, after that, I created a superuser and went to admin to add some examples to the database. However, I added some new rows on the Like table but does not display the total of likes for each post.
Your totalLikesSinglePost is just a list of Like records of the last post.
Try this on your html file:
<strong style="color: red">{{ post.post_like.count }}</strong>
Because totalLikesSinglePost get override in each round of for loop. It is the total number of likes of the last post.
You need to assign this number to the post as an attribute, like
for post in posts:
post.num_likes = Like.objects.all().filter(post=post).count()
And then in your template
{% for post in posts %}
{# ... #}
{{ post.num_likes }}
{% endfor %}
However, I strongly recommend you using .annotate
from django.db.models import Count
posts = Post.objects.all().order_by('-id')\
.annotate(num_likes=Count('like'))
This will access database only once and save you a lot of time.

why are comment objects not deleted from database although the view is triggered?

I try to remove comment so, I tried the first time by the class-based view then I hashed it to try the second way by normal function to know what's going on here. so, when I try to delete the comment by ID nothing does happen it's just directing me to the web page without deleting the comment so in this case the program runs but doesn't remove the object so, what is going on here?
Note: the posts and comments on the same page and slug field on that page are following by post not comment.
if the title of the post is: new title so, the slug will be new-title depending on the post
question_view.html
<div class="user-answer">
<div class="row">
<div class="col-xs-12">
{% for comment in my_question.comment_set.all %}
<div class="comments">
<div class="col-xs-0">
<div class="avatar">
<a href="{% url 'account:view_profile' comment.username %}">
<img class="img-circle img-thumbnail" style="width:50px; height: 50px;" src="{{ comment.logo }}">
</a>
</div>
</div>
<div class="col-xs-10">
<!-- --Comment itself-- -->
<div class="user_comment">
<p>{{ comment }}</p>
<div class="fa fa-caret-left comment-arrow"></div>
</div>
<!-- start Options in comment -->
<div class="sub-options">
{% if request.user.username == comment.username %}
<!-- --Edit comment-- -->
<div class="edit-comment">
<a>Edit</a>
</div>
<!-- --Delete comment-- -->
<div class="delete-comment">
<form method="post" action="{% url 'community:delete_comment' comment.pk %}">
{% csrf_token %}
<input type="hidden" name="delete-comment" value="{{ comment.comment }}">
<input type="submit" value="delete">
</form>
</div>
{% endif %}
<!-- end Options in comment -->
<!-- --comment Date-- -->
<div style="display: inline-block;color: #8e8e8e" class="comment-date">
<p>{{ comment.date|time }}</p>
</div>
</div>
</div>
<div class="clearfix"></div>
</div>
{% endfor %}
</div>
</div>
</div>
views.py
# Delete post
# class DeleteComment(DeleteView, SingleObjectMixin):
# model = Comment
# pk_url_kwarg = 'pk'
# template_name = 'community/question_view.html'
# queryset = Comment.objects.all()
#
# def get_success_url(self):
# title = UserAsking.objects.get(title=self.object)
# slug = UserAsking.objects.get(title=title).ask_slug
# return reverse_lazy('community:question_view', kwargs={'user_slug': slug})
#
# def get_object(self, queryset=None):
# request_comment = self.request.POST['delete-comment']
# return self.get_queryset().filter(pk=request_comment).get()
def delete_comment(request, pk):
if request.method == 'POST':
comment = Comment.objects.get(pk=pk)
del comment
return redirect('community:user_questions')
urls.py
urlpatterns = [
# path('delete-comment/<int:pk>/', views.DeleteComment.as_view(), name="delete_comment"),
path('delete-comment/<int:pk>/', views.delete_comment, name="delete_comment"),
]
models.py
class Comment(models.Model):
userasking = models.ForeignKey(UserAsking, on_delete=models.CASCADE)
comment = models.TextField(max_length=500, blank=True)
date = models.DateTimeField(auto_now_add=True)
userprofile = models.ForeignKey(UserProfile, on_delete=models.CASCADE, default=1)
name = models.CharField(max_length=30, blank=False, default='empty')
logo = models.ImageField(upload_to='images/', default='images/default-logo.jpg', blank=True)
username = models.CharField(max_length=30, blank=False, default='empty')
def __str__(self):
return self.comment
I hope if you can explain by class based-view what's happening I will appreciate that.
note that: if you could understand exactly what's happening you will know that is no error appears to me, therefore, I get no exception or traceback. thank you in advance.
Try this:
def delete_comment(request, pk):
if request.method == 'POST':
comment = Comment.objects.get(pk=pk).delete()
return redirect('community:user_questions')
Your function only deletes the object but not the database entry since you don't trigger a valid SQL operation (delete in this case).
You just delete the object comment again which was assigned previously, but you don't affect the database entry:
def delete_comment(request, pk):
if request.method == 'POST':
comment = Comment.objects.get(pk=pk)
del comment
return redirect('community:user_questions')
More on delete() in the official docu:
Deleting objects¶
Update on your comment:
You can't use Python's del statement to interact with database entries in Django.
Pythons del statement is used to delete objects initially created by Python like lists, variables, etc. etc.
In order to interact with your database in Django, you have to use the toolbox of Django's built-in Model instance which basically translates raw SQL operations into easy to use methods.
So maybe you can adapt a proper wording to highlight the differences by calling database "objects" entries and python objects: well, objects..
However, Django still offers the option to use raw SQL as fallback.

Django: export csv file with django_table2

I want to have an download button a html page that renders a django table.
I followed the documentation of django2 and this post How to export .csv with Django-Tables2? was helpful but could not make the trick.
I feel like I have done everything correctly (according to my beginner skills), there is no error but the download button is not there.
I was wondering if someone has any help to provide on this one
table.py
class AnormalTable(tables.Table):
class Meta:
model = stock_anormal
template_name = "django_tables2/bootstrap4.html"
export_formats = ['csv', 'xlsx']
view.py
#method_decorator(login_required, name='dispatch')
class PostDetailalerte_negat(LoginRequiredMixin,APIView, tables.SingleTableMixin, ExportMixin):
def get(self, request):
queryset = stock_negatif.objects.all()
table = NegatTable(queryset)
RequestConfig(request).configure(table)
export_format = request.GET.get("_export", None)
if TableExport.is_valid_format(export_format):
exporter = TableExport(export_format, table)
return exporter.response("table.{}".format(export_format))
return render(request, 'detailstocknegat.html', {'table':table})
html snipet
<div class="d-sm-flex align-items-center justify-content-between mb-4">
<h1 class="h3 mb-0 text-gray-800">ITEMS IN ALERTE SAFETY STOCK LEVEL</h1>
<div>
{% for format in view.export_formart %}
<i class="fas fa-download fa-sm text-white-50"></i> Generate Report
{% endfor %}
</div>
</div>
<table>
{% load django_tables2 %}
{% render_table table %}
</table>
I'm not sure if this will fix this specific problem but I ran into basically the same issue and this is how I solved it:
I moved the export_formats = ['csv', 'xlsx'] that was in table.py originally into the view for my table (view.py in this example). Then I passed export_formats directly to the html by changing the return code in views.py : return render(request, 'detailstocknegat.html', {'table':table, 'export_formats':export_formats}).
Then in the html for my table (html snipet in this example) I changed the for statement to be:
{%for format in export_formats %}>
<a href="{% export_url format %}">
download <code>.{{ format }}</code>
</a>
{% endfor %}
I hope this helps somebody out there!

How to display results from a database in a django web page?

I have a web page built in django that shows all rows of a sqlite table in bootstrap cards. Basically each card is a row of the table.
I want to give the user the ability to filter these cards, so the page instead of showing every objects in the database's table should display only the objects relevant to the user. The result of the search can be displayed at another URL, it's irrelevant.
I can build a html form to get info from the user and I can write a python function that search the database and print out specific rows based on an input keyword,
but I can't make these things work together.
Any hint or piece of code would be a great help, unfortunately I know nothing about php so if there's a way to do so without it would be great. Thanks all.
# this is my view function, it shows all the objects in the table at a #specific URL
def index_u(request):
universities = University.objects.all()
return render(request, 'index_uni.html', {'universities': universities})
/* this is index_uni.html */
{% block content %}
<div class="row">
{% for university in universities %}
<div class="col">
<div class="card" style="width: 18rem;">
<img src="{{ university.image_url}}" class="card-img-top">
<div class="card-body">
<h5 class="card-title">{{university.name}}</h5>
<p class="card-text">{{university.region}}</p>
Go
</div>
</div>
</div>
{% endfor %}
</div>
{% endblock %}
#this function takes an input and diplays specific rows
#I don't know how make this works in the html page, it works fine in terminal
def search_data():
region_choice = input("insert region here: ")
command = "SELECT name FROM university_university WHERE region = ?;"
c.execute(command, [region_choice])
for row in c:
print(row)
#here my model
class University(models.Model):
name = models.CharField(max_length=58, default=True)
city = models.CharField(max_length=58, default=True)
region = models.CharField(max_length=58, default=True)
country = models.CharField(max_length=58, default=True)
number_of_students = models.IntegerField(default=True)
image_url = models.CharField(max_length=2083, default=True)
is_public = models.BooleanField(default=True)
ranking = models.IntegerField(default=True)
colleges = models.BooleanField(default=True)
scholarship = models.BooleanField(default=True)
uni_canteen = models.BooleanField(default=True)
page_url = models.CharField(max_length=2083, default=True)
You can make a query with:
def index_u(request):
region = request.GET.get('region')
universities = University.objects.all()
if region is not None:
universities = universities.filter(region=region)
return render(request, 'index_uni.html', {'universities': universities})
You can then create for example a form, like:
<form method="get" action="url-of-index_u">
<input type="text" name="region">
<input type="submit">
</form>
<! -- render the universities -->

pass a value from html link to my function in flask

I want to pass a value from html link to my function in flask:
the police man name has a button next to it if it is clicked i should take its id and send it to the python function here is the html code :
<ul class="list-group">
{% for p in policemen %}
<li class="list-group-item">{{ " "~ p.rank ~" "}}
<div class="col-md-2 text-center">
<a href='/deletepolice/{{p.id}}' class="btn btn-primary btn-lg" >
</div> Delete ?
</a>
</li>
{% endfor %}
here is the python function :
#app.route('/deletepolice/<string:id>', methods=['GET','POST'])
def deletepolice(id):
# Create cursor
cur = mysql.connection.cursor()
# Execute
cur.execute("DELETE FROM police WHERE id = %s", [id])
# Commit to DB
mysql.connection.commit()
#Close connection
cur.close()
flash('Police man deleted', 'success')
return redirect(url_for('policemen'))
This should work. At the moment though the render_template of your HTML is not shown. Make sure that you are correctly passing the id to your a tag. When clicked the current way you have your a tag setup should lead you to that route.
Are you getting errors when you click the link? If so what are they?

Categories