How do I add pagination to this form's search functionality?
By default, on page refresh, the HTML template for loop shows all the results for all the courses. When the user types in criteria into the form, the form filters the course results template for loop based on what the user typed. Is there a way on page refresh to show only a set limit of course results on the page instead of all of them? Then when a user searches/filters, that pagination limit of X number of course results on a page will still need to show but still applied to the search criteria/filter.
HTML:
<form id='courseform' action="." method="get">
<div class="form-row">
<div class="form-group col-12"> {{ form.title__icontains }} </div>
<div class="form-group col-md-2 col-lg-2"> {{ form.visited_times__gte.label_tag }} {{ form.visited_times__gte }} </div>
<div class="form-group col-md-2 col-lg-2"> {{ form.visited_times__lte.label_tag }} {{ form.visited_times__lte }} </div>
<div class="form-group col-md-2 col-lg-2"> {{ form.created_at__gte.label_tag }} {{ form.created_at__gte }} </div>
<div class="form-group col-md-2 col-lg-2"> {{ form.created_at__lte.label_tag }} {{ form.created_at__lte }} </div>
<div class="form-group col-md-2"> {{ form.skill_level.label_tag }} {{ form.skill_level }} </div>
<div class="form-group col-md-2"> {{ form.subjects.label_tag }} {{ form.subjects }} </div>
</div>
<script src='https://www.google.com/recaptcha/api.js?render=6LeHe74UAAAAAKRm-ERR_fi2-5Vik-uaynfXzg8N'></script>
<div class="g-recaptcha" data-sitekey="6LeHe74UAAAAAKRm-ERR_fi2-5Vik-uaynfXzg8N"></div>
<button type="submit" class="btn btn-primary form-control">Search</button>
<p>This site is protected by reCAPTCHA and the Google
<a target="_blank" rel="noopener noreferrer" href="https://policies.google.com/privacy">Privacy Policy</a> and
<a target="_blank" rel="noopener noreferrer" href="https://policies.google.com/terms">Terms of Service</a> apply.
</p>
</form>
<!--Used to have {{ object.subjects }} next to -->
{% for object in object_list %}
<a class="course_list_link" href="{{ object.get_absolute_url }}"> <p class = "course_list_border"> <strong> {{ object }} </strong> <br/> <br/> {{ object.description }} <br/><br/> {{ object.skill_level }} {{ object.created_at }} Views: {{ object.visited_times }}
{% for sub in object.subjects.all %}
{{ sub.name }}
{% endfor %} </p> </a>
{% endfor %}
Views.py:
class CourseListView(ListView):
template_name = 'courses/course_list.html'
def get_queryset(self):
return Course.objects.all()
def get(self, request, *args, **kwargs):
form = CourseForm(request.GET)
queryset = self.get_queryset()
if form.is_valid():
queryset = queryset.filter(**{k: v for k, v in form.cleaned_data.items() if v})
self.object_list = queryset
return self.render_to_response(self.get_context_data(form=form,object_list=queryset,))
Forms.py:
class CourseForm(forms.Form):
title__icontains = forms.CharField(widget=forms.TextInput(attrs={'class':'form-control col-12', 'autocomplete':'off', 'id':'title_contains', 'type':'search', 'placeholder': 'Course Name'}), required=False)
visited_times__gte = forms.IntegerField(widget=forms.NumberInput(attrs={'class':'form-control', 'autocomplete':'off','id':'view_count_max', 'type':'number', 'min':'0', 'placeholder': '0'}), required=False, validators=[MinValueValidator(0), MaxValueValidator(99999999999999999999999999999999999)])
visited_times__lte = forms.IntegerField(widget=forms.NumberInput(attrs={'class':'form-control', 'autocomplete':'off', 'id':'view_count_min', 'type':'number', 'min':'0', 'placeholder': '1000000'}), required=False, validators=[MinValueValidator(0), MaxValueValidator(99999999999999999999999999999999999)])
created_at__gte = forms.DateField(widget=forms.TextInput(attrs={'class':'form-control', 'autocomplete':'off', 'id':'date_max','type':'date', 'placeholder': 'mm/dd/yyy'}), required=False)
created_at__lte = forms.DateField(widget=forms.TextInput(attrs={'class':'form-control', 'autocomplete':'off', 'id':'date_min', 'type':'date', 'placeholder': 'mm/dd/yyy'}), required=False)
skill_level = forms.ChoiceField(widget=forms.Select(attrs={'class':'form-control', 'autocomplete':'off','id':'skill_level'}), choices = ([('',''), ('Beginner','Beginner'), ('Intermediate','Intermediate'),('Advanced','Advanced'), ]), required=False)
subjects = forms.ModelChoiceField(queryset=Subject.objects.all().order_by('name'), empty_label="", widget=forms.Select(attrs={'class':'form-control', 'autocomplete':'off', 'id':'subjects'}), required=False)
# the new bit we're adding
def __init__(self, *args, **kwargs):
super(CourseForm, self).__init__(*args, **kwargs)
self.fields['title__icontains'].label = "Course Name:"
self.fields['visited_times__gte'].label = "Min Views:"
self.fields['visited_times__lte'].label = "Max Views:"
self.fields['created_at__gte'].label = "Min Date:"
self.fields['created_at__lte'].label = "Max Date:"
self.fields['skill_level'].label = "Skill Level:"
self.fields['subjects'].label = "Subject:"
self.fields['subjects'].queryset = Subject.objects.filter()
Models.py:
class Subject(models.Model):
SUBJECT_CHOICES = ()
name = models.CharField(max_length=20,choices=SUBJECT_CHOICES)
def __str__(self):
return self.name
class Meta:
ordering = ('name',)
class Course(models.Model):
SKILL_LEVEL_CHOICES = (
('Beginner', 'Beginner'),
('Intermediate', 'Intermediate'),
('Advanced', 'Advanced'),
)
slug = models.SlugField()
title = models.CharField(max_length=120)
description = models.TextField()
allowed_memberships = models.ManyToManyField(Membership)
created_at = models.DateTimeField(auto_now_add=True)
subjects = models.ManyToManyField(Subject)
skill_level = models.CharField(max_length=20,choices=SKILL_LEVEL_CHOICES, null=True)
visited_times = models.PositiveIntegerField(default=0)
def __str__(self):
return self.title
def get_absolute_url(self):
return reverse('courses:detail', kwargs={'slug': self.slug})
#property
def lessons(self):
return self.lesson_set.all().order_by('position')
class Meta:
ordering = ('title',)
I tried following some tutorials Django docs one but not sure how to embed that pagination code to mine.
https://docs.djangoproject.com/en/3.0/topics/pagination/
I would appreciate if anyone could help me with this.
Please edit to include your code.
In the page you linked to, read this section:
https://docs.djangoproject.com/en/3.0/topics/pagination/#paginating-a-listview
You just need to add this to your ListView
paginate_by = N
then add
<div class="pagination">
<span class="step-links">
{% if contacts.has_previous %}
« first
previous
{% endif %}
<span class="current">
Page {{ contacts.number }} of {{ contacts.paginator.num_pages }}.
</span>
{% if contacts.has_next %}
next
last »
{% endif %}
</span>
</div>
to course_list.html.
Serach form (html) :
<!-- Search Form -->
<form class="navbar-search navbar-search-dark form-inline mr-3 d-none d-md-flex ml-lg-auto" action="{% url 'search' %}" method="get">
<div class="form-group mb-0">
<input class="form-control mr-sm-2" type="search" placeholder="Search Tasks" aria-label="Search" name="q">
<button class="btn btn-outline-white my-2 my-sm-0" type="submit">Search</button>
</div>
</form>
views.py
class SearchView(ListView): # taskları arama sonucu listeliyoruz
model = Task
template_name = 'task/search.html'
paginate_by = 5
context_object_name = 'task'
def get_queryset(self):
query = self.request.GET.get("q")
if query:
return Task.objects.filter(
Q(title__icontains=query) | Q(content__icontains=query) | Q(tag__title__icontains=query)).order_by(
'id').distinct()
return Task.objects.all().order_by('id')
In whichever field you want to use pagination, write the code there (example category_detail.html) :
<div class="card-footer py-4">
{% if is_paginated %}
<nav aria-label="...">
<ul class="pagination justify-content-end mb-0">
{% if page_obj.has_previous %}
<li class="page-item">
<a class="page-link" href="?page={{ page_obj.previous_page_number }}" tabindex="-1">
<i class="fas fa-angle-left"></i>
<span class="sr-only">Previous</span>
</a>
</li>
{% else %}
<li class="page-item disabled">
<a class="page-link" href="#" tabindex="-1">
<i class="fas fa-angle-left"></i>
<span class="sr-only">Previous</span>
</a>
</li>
{% endif %}
{% for i in paginator.page_range %}
{% if page_obj.number == i %}
<li class="page-item active">
<a class="page-link" href="#"> {{ i }} </a>
</li>
{% else %}
<li class="page-item">
<a class="page-link" href="?page={{ i }}">{{ i }}<span class="sr-only">(current)</span></a>
</li>
{% endif %}
{% endfor %}
{% if page_obj.has_next %}
<li class="page-item">
<a class="page-link" href="?page={{ page_obj.next_page_number }}">
<i class="fas fa-angle-right"></i>
<span class="sr-only">Next</span>
</a>
</li>
{% else %}
<li class="page-item disabled">
<a class="page-link" href="#">
<i class="fas fa-angle-right"></i>
<span class="sr-only">Next</span>
</a>
</li>
{% endif %}
</ul>
</nav>
{% endif %}
</div>
I hope the examples worked for you.
Related
I made a blog app in which I want to add pagination both on home page and category page. but after a lot of efforts I didn't get how to use it in the category page. I use 2 parameters in category view and I don' know how to fix it now. whenever I click on category "That page number is not an integer" displays.
views.py:
def allpost(request):
postData = Post.objects.all()
paginator = Paginator(postData, 5) # Show 5 contacts per page.
page_number = request.GET.get('page')
page_obj = paginator.get_page(page_number)
return render(request, 'posts.html', {'page_obj': page_obj})
def CategoryView(request, cats='category__title'):
category_posts = Post.objects.all()
paginator = Paginator(category_posts, 5)
# We don't need to handle the case where the `page` parameter
# is not an integer because our URL only accepts integers
try:
category_posts = paginator.page('cats')
except EmptyPage:
# if we exceed the page limit we return the last page
category_posts = paginator.page(paginator.num_pages)
return render(request, 'categories.html', {'category_posts': category_posts})
urls.py:
path('category/<str:cats>/', views.CategoryView, name ="category"),
categories.html
{% extends 'base.html' %}
{%block content%}
<h1> Category: {{ cats }} </h1>
{% for post in category_posts %}
<div class="container mt-3">
<div class="row mb-2">
<div class="col-md-6">
<div class="card flex-md-row mb-4 box-shadow h-md-250">
<div class="card-body d-flex flex-column align-items-start">
<strong class="d-inline-block mb-2 text-primary">{{ post.category }}</strong>
<h3 class="mb-0">
<a class="text-dark" href="{% url 'detail' post.id %}">{{post.title}}</a>
</h3>
<div class="mb-1 text-muted">{{ post.public_date }}</div>
<p class="card-text mb-auto">{{ post.summary }}</p>
Continue reading
</div>
<img class="card-img-right flex-auto d-none d-md-block" data-src="holder.js/200x250?theme=thumb" alt="Thumbnail [200x250]" style="width: 200px; height: 250px;" src="data:image/svg+xml;charset=UTF-8,%3Csvg%20width%3D%22200%22%20height%3D%22250%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%20200%20250%22%20preserveAspectRatio%3D%22none%22%3E%3Cdefs%3E%3Cstyle%20type%3D%22text%2Fcss%22%3E%23holder_182c981dfc3%20text%20%7B%20fill%3A%23eceeef%3Bfont-weight%3Abold%3Bfont-family%3AArial%2C%20Helvetica%2C%20Open%20Sans%2C%20sans-serif%2C%20monospace%3Bfont-size%3A13pt%20%7D%20%3C%2Fstyle%3E%3C%2Fdefs%3E%3Cg%20id%3D%22holder_182c981dfc3%22%3E%3Crect%20width%3D%22200%22%20height%3D%22250%22%20fill%3D%22%2355595c%22%3E%3C%2Frect%3E%3Cg%3E%3Ctext%20x%3D%2256.20000076293945%22%20y%3D%22131%22%3EThumbnail%3C%2Ftext%3E%3C%2Fg%3E%3C%2Fg%3E%3C%2Fsvg%3E" data-holder-rendered="true">
</div>
</div>
</div>
</div>
{% endfor %}
{% include 'pagination.html' %}
{%endblock%}
pagination.html
<div class="container mt-3">
<nav aria-label="Page navigation example">
<ul class="pagination">
{% if page_obj.has_previous %}
<li class="page-item"><a class="page-link" href="/?page=1">First</a></li>
<li class="page-item"><a class="page-link" href="/?page={{ page_obj.previous_page_number }}">Previous</a></li>
{% endif %}
<li class="page-item"><a class="current" href="/?page={{ page_obj.number }} of {{ page_obj.paginator.num_pages }}">1</a></li>
{% if page_obj.has_next %}
<li class="page-item"><a class="page-link" href="/?page={{ page_obj.next_page_number }}">Next</a></li>
<li class="page-item"><a class="page-link" href="/?page={{lastpage}}">Last</a></li>
{% endif %}
</div>
I am very confused already in category views function to use parameter issue. so that I didn't use that code in pagination.html.
===== in view.py ======
from django.core.paginator import Paginator
def HomeView(request):
show_data = VehicleModel.objects.all() # Queryset For pagiantion
# Pagination code start
paginator = Paginator(show_data, 3, orphans=1)
page_number = request.GET.get('page')
show_data = paginator.get_page(page_number)
# Pagination code end
context = {'page_number':page_number}
return render(request,'dashboard.html',context)
===== in html file ======
<!-- Pagination Block with page number -->
<div class="container mt-5">
<div class="row float-right ">
<span class="m-0 p-0">
{% if carset.has_previous %} # <!-- For Previous Button -->
<a class="btn btn-outline-info" href="?page={{carset.previous_page_number}}&ok=#ok">Previous</a>
{% endif %}
<span>{% for pg in carset.paginator.page_range %} # <!-- For Page Numbers Buttons -->
{% if carset.number == pg %}
<a href="?page={{pg}}" class="btn btn-sm btn-primary">
<span class="badge">{{pg}}</span>
</a>
{% else %}
<a href="?page={{pg}}" class="btn btn-sm btn-secondary">
<span class="badge">{{pg}}</span>
</a>
{% endif %}
{% endfor %}</span>
{% if carset.has_next %} # <!-- For Next Button -->
<a class="btn btn-outline-info" href="?page={{carset.next_page_number}}&ok=#ok">Next</a>
{% endif %}
</span>
</div>
</div>
=========== Your code is like this ==============
here you passed the category parameter in the function so must pass the category title with the calling URL of this CategoryView(request, cats='category__title') function
def CategoryView(request, cats='category__title'):
category_posts = Post.objects.all()
paginator = Paginator(category_posts, 5)
with this CategoryView(request, cats='category__title') you want all Posts ???
def
CategoryView(request, cats='category__title'):
category_posts = Post.objects.filter(cats__title = cats)
paginator = Paginator(category_posts, 5)
ator = Paginator(category_posts, 5)
i have a django model as follows in which i defined categories
CATEGORY_CHOICES = (
('BS', 'Best Selling'),
('TP', 'Trending Product'),
('RP', 'Related Products'),
('NA', 'New Arrival'),
('F', 'Featured'),
('OS', 'on sale'),)
class Item(models.Model):
title = models.CharField(max_length=100)
price = models.FloatField()
discount_price = models.FloatField(blank=True, null=True)
category = models.CharField(choices=CATEGORY_CHOICES, max_length=2)
label = models.CharField(choices=LABEL_CHOICES, max_length=1)
slug = models.SlugField()
description = models.TextField()
image = models.ImageField()
def __str__(self):
return self.title
def get_absolute_url(self):
return reverse("core:product", kwargs={
'slug': self.slug
})
def get_add_to_cart_url(self):
return reverse("core:add-to-cart", kwargs={
'slug': self.slug
})
def get_remove_from_cart_url(self):
return reverse("core:remove-from-cart", kwargs={
'slug': self.slug
})
**and in my Django home page there are multiples sections based on categories like trending product, on sale, featured, new arrivals etc ** what I wanted is to filter data based on that categories and show to the respective section and for that, I registred a template as follows:
#register.filter
def in_category(Item, category):
return Item.filter(category=category)
and on my home template i tried to use this filter as follows:
{% for object_list in object_list|in_category:Featured %}
<div class="col-3">
<div class="custom-col-5">
<div class="single_product">
<div class="product_thumb">
<a href="{{ item.get_absolute_url }}" class="primary_img"><img src="{{ item.image.url }}"
alt="product1"></a>
<a href="{{ item.get_absolute_url }}" class="secondary_img"><img src="{{ item.image.url }}"
alt="product1"></a>
<div class="quick_button">
<a href="{{ item.get_absolute_url }}" data-toggle="modal" data-target="#modal_box"
data-placement="top" data-original-title="quick view">Quick
View</a>
</div>
</div>
<div class="product_content">
<div class="tag_cate">
Ring, Necklace,
Earrings
</div>
<h3>{{ item.title }}</h3>
<div class="price_box">
<span class="old_price">Rs. 45654</span>
<span class="current_price">{% if item.discount_price %}
{{ item.discount_price }}
{% else %}
{{ item.price }}
{% endif %}</span>
</div>
<div class="product_hover">
<div class="product_ratings">
<ul>
<li><i class="ion-ios-star-outline"></i>
</li>
<li><i class="ion-ios-star-outline"></i>
</li>
<li><i class="ion-ios-star-outline"></i>
</li>
<li><i class="ion-ios-star-outline"></i>
</li>
<li><i class="ion-ios-star-outline"></i>
</li>
</ul>
</div>
<div class="product_desc">
<p>This is a gold ring with diamond and Lorem ipsum
dolor sit amet.</p>
</div>
<div class="action_links">
<ul>
<li><a href="#" data-placement="top" title="Add to Wishlist" data-toggle="tooltip"><span
class="ion-heart"></span></a></li>
<li class="add_to_cart"><a href="#" title="Add to Cart">Add
to Cart</a></li>
<li><i class="ion-ios-settings-strong"></i>
</li>
</ul>
</div>
</div>
</div>
</div>
</div>
</div>
{% endfor %}
but django says it is an error VariableDoesNotExist at /
Failed lookup for key [Featured] in
can sombody help me with this thanks in advance.
You can use simple for loop and condition
{% if item.category == "Featured" %}
---------html for featured categ---
{% endif %}
{% if item.category == "onsale" %}
---------html for onsale categ---
{% endif %}
.
.
.
{% endfor %}```
I have problem. I want create relationship in model Django. I would like every user to have a different product price. The product must be assigned to the user. It must is to display only after logging in. Unfortunately, the template does not show all parameters.
offers/models.py
class Product(models.Model):
title = models.CharField(max_length=100)
category = models.CharField(max_length=50)
weight = models.FloatField()
description = models.TextField(blank=True)
photo = models.ImageField(upload_to='photos/%Y/%m/%d/')
is_published = models.BooleanField(default=True)
list_date = models.DateField(default=datetime.now, blank=True)
def __str__(self):
return self.title
class UserProduct(models.Model):
user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
title = models.ForeignKey(Product, on_delete=models.DO_NOTHING)
price = models.FloatField()
is_published = models.BooleanField(default=True)
list_date = models.DateField(default=datetime.now, blank=True)
def __str__(self):
return str(self.user.username) if self.user.username else ''
Products are to be displayed for logged in users.
offers/views.py
def index(request):
context = {
'products': Product.objects.order_by('category').filter(is_published=True)
}
return render(request, 'offers/products.html', context)
def userproduct(request):
context = {
'userproduct': UserProduct.objects.filter(user_id=request.user.id),
'userproduct1': Product.objects.all()
}
return render(request, 'offers/userproducts.html', context)
My template show only title, and price.
offers/userproducts.html
<!-- Offers -->
<section class="text-center mb-4 py-4">
<div class="container">
<div class="row">
{% if user.is_authenticated %}
{% if userproduct %}
{% for product in userproduct %}
<!--Grid column-->
<div class="col-lg-3 col-md-6 mb-4">
<div class="card">
<div class="view overlay">
<img src="{{ product.photo.url }}" class="card-img-top" alt="">
<a href="{% url 'product' product.id %}">
<div class="mask rgba-white-slight"></div>
</a>
</div>
<div class="card-body text-center">
<h6 class="grey-text">{{ product.category }}</h6>
<h5>
<strong>
{{ product.title }}
</strong>
</h5>
<h4 class="font-weight-bold colores">
<strong>{{ product.price }}</strong>
</h4>
</div>
</div>
</div>
<!--Grid column-->
{% endfor %}
{% else %}
<div class="col-sm-12 sm-12">
<p>No Products Available XD</p>
</div>
{% endif %}
{% endif %}
</div>
</div>
</section>
The userproduct queryset that you are iterating over belongs to UserProduct model which doesn't have the fields you're looking for.
This is how it should be:
<!-- Offers -->
<section class="text-center mb-4 py-4">
<div class="container">
<div class="row">
{% if user.is_authenticated %}
{% if userproduct %}
{% for product in userproduct %}
<!--Grid column-->
<div class="col-lg-3 col-md-6 mb-4">
<div class="card">
<div class="view overlay">
<img src="{{ product.title.photo.url }}" class="card-img-top" alt="">
<a href="{% url 'product' product.title.id %}">
<div class="mask rgba-white-slight"></div>
</a>
</div>
<div class="card-body text-center">
<h6 class="grey-text">{{ product.title.category }}</h6>
<h5>
<strong>
{{ product.title.title }}
</strong>
</h5>
<h4 class="font-weight-bold colores">
<strong>{{ product.price }}</strong>
</h4>
</div>
</div>
</div>
<!--Grid column-->
{% endfor %}
{% else %}
<div class="col-sm-12 sm-12">
<p>No Products Available XD</p>
</div>
{% endif %}
{% endif %}
</div>
</div>
</section>
If you need a field from Product model, you should get the Product instance which is title. So:
Price: product.price
Title: product.title.title (product.title also works because of your __str__ function.)
Category: product.title.category
Photo: product.title.photo
And so on ...
I have a blog-type application where the homepage displays posts with some information and with an Add Comment form. The form is intended to write to my Comments() model in models.py for that specific post.
The Problem: is that because I am looping through the posts in home.html, the post.id is not available to the home function in routes.py. So, when the form is validated, the comment is applied to all the posts, not just the one in which the comment is added.
The question: how can I get the relevant post.id in the home function from the Jinja forloop and have the comment be applied to the specific post, not just all the posts on the homepage?? I'm not seeing my logic/syntax error - what am I missing here? Thanks
The resulting error: AttributeError: 'function' object has no attribute 'id'
which of course makes sense because the application has no clue what post we are referencing in the Jinja2 forloop in home.html.
here is the Comments db model in models.py:
class Comments(db.Model):
comment_id = db.Column(db.Integer, primary_key=True)
user_id = db.Column(db.Integer, nullable=True, primary_key=False)
post_id = db.Column(db.Integer, nullable=True, primary_key=False)
comment = db.Column(db.String(2000), unique=False, nullable=True)
comment_date = db.Column(db.DateTime, nullable=False, default=datetime.utcnow)
class Meta:
database = db
def fetch_post_comments(self, post_id):
comments = Comments.query.filter(Comments.post_id==post_id)
return comments
def fetch_user(self, user_id):
user = User.query.filter(User.id==user_id).first_or_404()
return user.username
def __repr__(self):
return f"Comments ('{self.user_id}', '{self.post_id}', '{self.comment}', '{self.comment_date}')"
And here is my home function in routes.py:
#app.route("/")
#app.route("/home", methods=['GET', 'POST'])
#login_required
def home():
page = request.args.get('page', 1, type=int)
posts = Post.query.order_by(Post.date_posted.desc()).paginate(page=page, per_page=5)
comment_form = CommentForm()
def add_comment(post_id):
if comment_form.validate_on_submit():
new_comment = Comments(user_id=current_user.id,
post_id=post_id,
comment=comment_form.comment_string.data)
db.session.add(new_comment)
db.session.commit()
flash('HOT TAKE! Posted your comment.', 'success')
# return redirect(url_for('post', post_id=post.id))
def get_upvote_count(post_id):
count = Vote.query.filter(Vote.post_id==post_id).count()
return count
def get_flag_count(post_id):
count = Flag.query.filter(Flag.post_id == post_id).count()
return count
def get_comment_count(post_id):
count = Comments.query.filter(Comments.post_id == post_id).count()
return count
def get_favorite_count(post_id):
count = Favorites.query.filter(Favorites.post_id == post_id).count()
return count
def get_youtube_id_from_url(url):
video_id = url.split('v=')[1]
if '&' in video_id:
video_id = video_id.split('&')[0]
base_url = "https://www.youtube.com/embed/"
return base_url + video_id
def get_spotify_embed_url(url):
track_or_playlist = url.split('https://open.spotify.com/')[1].split('/')[0]
base_url = f"https://open.spotify.com/embed/{track_or_playlist}/"
spotify_id = url.split('https://open.spotify.com/')[1].split('/')[1]
embed_url = base_url + spotify_id
return embed_url
return base_url + video_id
return render_template('home.html',
posts=posts,
get_upvote_count=get_upvote_count,
get_comment_count=get_comment_count,
get_flag_count=get_flag_count,
get_favorite_count=get_favorite_count,
comment_form=comment_form,
add_comment=add_comment,
get_youtube_id_from_url=get_youtube_id_from_url,
get_spotify_embed_url=get_spotify_embed_url)
And here is my home.html
{% extends "layout.html" %}
{% block content %}
{% for post in posts.items %}
<article class="media content-section">
<img class="rounded-circle article-img" src="{{ url_for('static', filename='profile_pics/' + post.author.image_file) }}">
<div class="media-body">
<div class="article-metadata">
<div align="left">
<a class="mr-2 text-secondary" href="{{ url_for('user_posts', username=post.author.username) }}">{{ post.author.username }}</a>
</div>
<div align="left">
<small class="text-muted">Posted on: {{ post.date_posted.strftime('%Y-%m-%d') }}</small>
</div>
<div align="right">
<a class="mr-2 text-secondary" href="{{ url_for('flag', post_id=post.id, user_id=current_user.id) }}">Flag Post</a>
</div>
<div align="right">
<a class="mr-2 text-secondary" href="{{ url_for('add_favorite', post_id=post.id, user_id=current_user.id) }}">Favorite Post ({{ get_favorite_count(post.id) }})</a>
</div>
<div align="right">
<a class="mr-2 text-secondary" href="{{ url_for('post', post_id=post.id) }}">Comments ({{ get_comment_count(post.id) }})</a>
</div>
</div>
<h3><a class="article-title" href="{{ url_for('post', post_id=post.id) }}">{{ post.title }}</a></h3>
<p class="article-content justify-content-center">{{ post.content }}</p>
<br>
{% for url in post.urls.split('||') %}
{% if 'youtube.com' in url %}
<div class="embed-responsive embed-responsive-16by9">
<iframe class="embed-responsive-item"
src="{{ get_youtube_id_from_url(url) }}" allowfullscreen="" frameborder="0">
</iframe>
</div>
Link
{% elif 'soundcloud.com' in url %}
<div class="embed-responsive embed-responsive-16by9">
<iframe class="embed-responsive-item" scrolling="no" frameborder="no"
src="{{ 'https://w.soundcloud.com/player/?url=' + url }}">
</iframe>
</div>
Link
{% elif 'spotify.com' in url %}
<div class="embed-responsive embed-responsive-16by9">
<iframe class="embed-responsive-item"
src="{{ get_spotify_embed_url(url) }}" allowfullscreen allow="encrypted-media">
</iframe>
</div>
Link
{% elif 'vimeo.com' in url %}
<div class="embed-responsive embed-responsive-16by9">
<iframe class="embed-responsive-item" scrolling="no" frameborder="no"
src="{{ 'https://player.vimeo.com/video/' + url.split('https://vimeo.com/')[1] }}">
</iframe>
</div>
Link
{% elif 'tumblr.com' in url %}
<div class="embed-responsive embed-responsive-16by9">
<iframe class="embed-responsive-item"
src="{{ url }}" frameborder="0">
</iframe>
</div>
Link
{% else %}
Link
<br>
{% endif %}
{% endfor %}
<br>
<br>
<p class="text-muted"><strong>Tags:</strong></p>
{% for tag in post.tags.replace(' ', ' ').strip(',').split(' ') %}
<a class="btn btn-light" href="{{url_for('tag_posts', tag=tag)}}">{{tag.strip('#').strip(' ').lower() }}</a>
{% endfor %}
<br>
<form method="POST" action="" enctype="multipart/form-data">
{{ comment_form.hidden_tag() }}
<fieldset class="form-group">
<br>
<br>
<p class="text-muted"><strong>Add a comment:</strong></p>
<div class="form-group">
{% if comment_form.comment_string.errors %}
{{ comment_form.comment_string(class="form-control form-control-lg is-invalid") }}
<div class="invalid-feedback">
{% for error in comment_form.comment_string.errors %}
<span>{{ error }}</span>
{% endfor %}
</div>
{% else %}
{{ comment_form.comment_string(class="form-control form-control-lg") }}
<!-- {{ add_comment(post_id=post.id) }}-->
{% endif %}
</div>
</fieldset>
<div class="form-group">
{{ comment_form.submit(class="btn btn-secondary") }}
</div>
</form>
<br>
<p class="text-muted mt-4"><strong>Street Cred: </strong>{{ get_upvote_count(post.id) }}</p>
<a class="btn btn-secondary mb-4" href="{{url_for('upvote', user_id=post.author.id, post_id=post.id)}}">Upvote</a>
</div>
</article>
{% endfor %}
{% for page_num in posts.iter_pages(left_edge=1, right_edge=1, left_current=1, right_current=2) %}
{% if page_num %}
{% if posts.page == page_num %}
<a class="btn btn-secondary mb-4" href="{{ url_for('home', page=page_num) }}">{{ page_num }}</a>
{% else %}
<a class="btn btn-outline-info mb-4" href="{{ url_for('home', page=page_num) }}">{{ page_num }}</a>
{% endif %}
{% else %}
...
{% endif %}
{% endfor %}
{% endblock content %}
A couple of options:
Add the post.id to the url as a parameter or arg, here's the arg method:
Add the url to the form and set the post.id as the arg:
<form method="POST" action="{{url_for('home', post_id=post.id)}}" enctype="multipart/form-data">
And in the route:
new_comment = Comments(user_id=current_user.id,
post_id=request.args.get('post_id', type=int),
comment=comment_form.comment_string.data)
OR
Add a hidden field to your form, and set the value of that to be the post.id:
Add a hidden field onto your form:
post_id = HiddenField()
You'll need to replace the CSRF render (hidden_tag()) to prevent auto rendering of the post_id field:
{{ comment_form.csrf_token }}
Next, set the value of your hidden field data (credit to this answer for this function):
{% set p = comment_form.post_id.process_data(post.id) %}
{{ comment_form.post_id() }}
and finally, in the route, (remove the add_comment declaration):
def home():
# Omitted ...
if comment_form.validate_on_submit():
new_comment = Comments(user_id=current_user.id,
post_id=comment_form.post_id.data,
comment=comment_form.comment_string.data)
db.session.add(new_comment)
# etc...
Hope this helps, note it's not tested
I am working with a CreateView. When sending my form with all fields properly filled out, the field tickets (ManyToMany field) is also saved. However if I POST my form with some validation error (e.g. required field empty), then all the pre-filled fields are still field through request.POST. However, my pre-checked fields are not selected anymore. Do you know why?
Demonstration
view.py
class DiscountCreate(AdminPermissionRequiredMixin, SuccessMessageMixin,
FormValidationMixin, BaseDiscountView, CreateView):
form_class = DiscountForm
template_name = 'discounts/admin/create.html'
success_message = _("Discount Code has been successfully created.")
def get_form_kwargs(self):
kwargs = super().get_form_kwargs()
kwargs['event'] = self.request.event
return kwargs
def get_success_url(self):
return reverse('discounts:admin:detail', kwargs={
'organizer': self.request.organizer.slug,
'event': self.request.event.slug,
'discount': self.instance.pk
})
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['type_fixed'] = Discount.TYPE_FIXED
context['type_percentage'] = Discount.TYPE_PERCENTAGE
return context
def form_valid(self, form):
self.instance = form.save(commit=False)
self.instance.event = self.request.event
self.instance.status = Discount.STATUS_ACTIVE
return super().form_valid(form)
Template
<div class="card">
<div class="card-header">
<h4 class="card-header-title">
{% trans "Creating Discount Code" %}
</h4>
</div>
<div class="card-body">
<form method="post" autocomplete="off" novalidate>
{% csrf_token %}
<div class="form-group">
<label for="{{ form.code.id_for_label }}">
{{ form.code.label }}
</label>
{{ form.code }}
{% if form.code.errors %}
<div class="invalid-feedback d-block">
{{ form.code.errors|first }}
</div>
{% endif %}
</div>
<div class="form-group">
<label for="{{ form.type.id_for_label }}">
{{ form.type.label }}
</label>
{{ form.type }}
{% if form.type.errors %}
<div class="invalid-feedback d-block">
{{ form.type.errors|first }}
</div>
{% endif %}
</div>
<div class="form-group fixed{% if form.type.value != type_fixed %} d-none {% endif %}">
{{ form.value.label_tag }}
<div class="input-group">
{{ form.value }}
<div class="input-group-append">
<span class="input-group-text text-dark">{{ request.event.currency }}</span>
</div>
</div>
{% if form.value.errors %}
<div class="invalid-feedback d-block">
{{ form.value.errors|first }}
</div>
{% endif %}
</div>
<div class="form-group percentage{% if form.type.value != type_percentage %} d-none {% endif %}">
{{ form.percentage.label_tag }}
<div class="input-group">
{{ form.percentage }}
<div class="input-group-append">
<span class="input-group-text text-dark">%</span>
</div>
</div>
{% if form.percentage.errors %}
<div class="invalid-feedback d-block">
{{ form.percentage.errors|first }}
</div>
{% endif %}
</div>
<div class="form-group">
{{ form.available_amount.label_tag }}
{{ form.available_amount }}
{% if form.available_amount.errors %}
<div class="invalid-feedback d-block">
{{ form.available_amount.errors|first }}
</div>
{% endif %}
</div>
<div class="form-group">
<label for="{{ form.tickets.id_for_label }}">
{{ form.tickets.label }}
<span class="badge badge-light">{% trans "Optional" %}</span>
</label>
<small class="form-text text-muted mt--2">{{ form.tickets.help_text }}</small>
{% for ticket_id, ticket_label in form.tickets.field.choices %}
<div class="custom-control custom-checkbox">
<input
type="checkbox"
name="{{ form.tickets.html_name }}"
value="{{ ticket_id }}"
class="custom-control-input"
id="id_{{ form.tickets.html_name }}_{{ forloop.counter }}"
{% if ticket_id in form.tickets.value %}checked{% endif %}>
<label class="custom-control-label" for="id_{{ form.tickets.html_name }}_{{ forloop.counter }}">{{ ticket_label }}</label>
</div>
{% endfor %}
{% if form.tickets.errors %}
<div class="invalid-feedback d-block">
{{ form.tickets.errors|first }}
</div>
{% endif %}
</div>
<div class="form-group">
<label for="{{ form.valid_from.id_for_label }}">
{{ form.valid_from.label }}
<span class="badge badge-light">{% trans "Optional" %}</span>
</label>
<small class="form-text text-muted mt--2">{{ form.valid_from.help_text }}</small>
{{ form.valid_from }}
{% if form.valid_from.errors %}
<div class="invalid-feedback d-block">
{{ form.valid_from.errors|first }}
</div>
{% endif %}
</div>
<div class="form-group">
<label for="{{ form.valid_until.id_for_label }}">
{{ form.valid_until.label }}
<span class="badge badge-light">{% trans "Optional" %}</span>
</label>
<small class="form-text text-muted mt--2">{{ form.valid_until.help_text }}</small>
{{ form.valid_until }}
{% if form.valid_until.errors %}
<div class="invalid-feedback d-block">
{{ form.valid_until.errors|first }}
</div>
{% endif %}
</div>
<div class="form-group">
<label for="{{ form.comment.id_for_label }}">
{{ form.comment.label }}
<span class="badge badge-light">{% trans "Optional" %}</span>
</label>
<small class="form-text text-muted mt--2">{{ form.comment.help_text }}</small>
{{ form.comment }}
{% if form.comment.errors %}
<div class="invalid-feedback d-block">
{{ form.comment.errors|first }}
</div>
{% endif %}
</div>
<button type="submit" class="btn btn-primary btn-block">{% trans "Create Discount Code" %}</button>
</form>
</div>
</div> {# / .card #}
<a class="btn btn-block btn-link text-muted mb-4" href="{% url 'discounts:admin:index' request.organizer.slug request.event.slug %}">
{% trans "Cancel discount code creation" %}
</a>
forms.py
class DiscountForm(forms.ModelForm):
# Remove required attribute from HTML elements
use_required_attribute = False
value = forms.DecimalField(decimal_places=2, required=False)
percentage = forms.DecimalField(max_digits=4, decimal_places=2, required=False)
class Meta:
model = Discount
fields = (
'code',
'type',
'value',
'percentage',
'available_amount',
'tickets',
'valid_from',
'valid_until',
'comment',
)
def __init__(self, *args, **kwargs):
self.event = kwargs.pop('event')
super().__init__(*args, **kwargs)
for visible_field in self.visible_fields():
visible_field.field.widget.attrs['class'] = 'form-control'
self.fields['tickets'].queryset = self.event.tickets.all()
self.fields['code'].widget.attrs['autofocus'] = True
self.fields['valid_from'].widget.attrs['class'] = 'form-control start-date picker'
self.fields['valid_until'].widget.attrs['class'] = 'form-control end-date picker'
self.fields['valid_from'].widget.format = settings.DATETIME_INPUT_FORMATS[0]
self.fields['valid_until'].widget.format = settings.DATETIME_INPUT_FORMATS[0]
self.fields['valid_from'].widget.attrs['data-lang'] = get_lang_code()
self.fields['valid_until'].widget.attrs['data-lang'] = get_lang_code()
def clean(self):
cleaned_data = super().clean()
discount_type = cleaned_data.get('type')
if discount_type:
if discount_type == Discount.TYPE_FIXED:
value = cleaned_data.get('value')
cleaned_data['percentage'] = None
if not value:
message = _("Please enter how much discount you want to give.")
self.add_error('value', forms.ValidationError(message))
if discount_type == Discount.TYPE_PERCENTAGE:
percentage = cleaned_data.get('percentage')
cleaned_data['value'] = None
if not percentage:
message = _("Please enter how much discount you want to give.")
self.add_error('percentage', forms.ValidationError(message))
def clean_value(self):
value = self.cleaned_data['value']
if value:
value = smallest_currency_unit_converter(
value,
self.event.currency,
)
return value
def clean_percentage(self):
percentage = self.cleaned_data['percentage']
if percentage:
percentage /= 100 # convert 19 to 0.19
return percentage
def clean_valid_until(self):
valid_until = self.cleaned_data['valid_until']
if valid_until and valid_until > self.event.end_date:
valid_until = self.event.end_date
return valid_until
def clean_valid_from(self):
valid_from = self.cleaned_data['valid_from']
if valid_from and valid_from > self.event.end_date:
raise forms.ValidationError(_("Discount code should become valid \
before the event starts."), code='valid_from')
return valid_from
def clean_code(self):
code = self.cleaned_data['code']
code_check = self.event.discounts.filter(
code=code
).exclude(pk=self.instance.pk).exists()
if code_check:
raise forms.ValidationError(_("The code you chose as your discount code \
already exists for this event. Please change it."), code='code_exists')
return code
After I broke the problem down, the final solution that helped me can be found here: Django template: {% if 5 in ['4', '3', '5'] %} doesn't work