Flask pagination, query issue, why so many items? - flask

I'm workin on a blog page and I'm trying to filter posts by tag, the problem is that I get several pages when only 1 or 2 posts match the query (I have per_page set to 6).
I have another filter by followed posts that works correctly, therefore I guess the problem is in the query object.
This is part of my code for the view index:
if current_user.is_authenticated:
show_followed = bool(request.cookies.get('show_followed', ''))
if show_followed:
query = current_user.followed_posts
elif show_tag:
tag = Tag.query.filter_by(tag_name=show_tag).first()
query = tag.post
else:
query = Post.query
page = request.args.get('page', 1, type=int)
pagination = query.order_by(Post.timestamp.desc()).paginate(
page, per_page=current_app.config['POSTS_PER_PAGE'],
error_out=False)
posts = pagination.items
return render_template("index.html", posts=posts, pagination = pagination, show_followed=show_followed, show_tag=show_tag)
when I try to replicate the issue in the console, I check that the query for a sample tag matches 3 posts, nevertheless pagination.total (total items) returns 23! What does the pagination object take as items??
I did this to test in the flask shell:
tagname = 'pruebatag'
tag = Tag.query.filter_by(tag_name=tagname).first()
query = tag.post
#here if y try query.all() I get 3 posts in return
pag_object = query.paginate(per_page = 6)
pag_object.total
>>> 23
pag_object.pages
>>> 4
pag_object.items #returns items for current page
>>> [<Post example>] #only one post returned for this page? why 4 pages?
I copy my definition of the tags table:
class Tag(db.Model):
__tablename__ = 'tags'
id = db.Column(db.Integer, primary_key=True)
tag_name = db.Column(db.String(40), unique=True)
def add_tag(self):
if not self.id:
db.session.add(self)
db.session.commit()
tag_join = db.Table('tag_join',
db.Column('post_id', db.Integer, db.ForeignKey('post.id')),
db.Column('tag_id', db.Integer, db.ForeignKey('tags.id'))
)
And in the post model:
tags = db.relationship('Tag',
secondary=tag_join,
backref=db.backref('post', lazy='dynamic'),
lazy='dynamic')
Any help would be highly appreciated as I am wasting a lot of time on this issue
Just let mi know if you need more details.
Thanks!

Finally I found what happens, I realize that table tag_join is full of duplicates, that cause the issue.

Related

django-filter not showing filtered items

I used django-filter in a similar way before and it worked fine, but now when I try to filter my posts it just returns all the items instead of filtering them, can anyone figure what I'm doing wrong?
my filters.py
class filtering(django_filters.FilterSet):
class Meta:
model = Cars
fields = ['color','body']
and my views:
def peugeot_206(request):
### Getting Last Month Records of 206 ###
start_date = (datetime.today() - timedelta(30))
end_date = (datetime.today() + timedelta(1))
last_month_records = Cars.objects.filter(datetime__range=(start_date, end_date),car='206')
post_list = last_month_records.order_by('-datetime').values()
### Calculating Aggregations ###
average_price = last_month_records.aggregate(Avg('price'))
min_price = last_month_records.aggregate(Min('price'))
max_price = last_month_records.aggregate(Max('price'))
filters = filtering(request.GET,queryset=post_list)
post_list = filters.qs.values()
### Pagination ###
paginator = Paginator(post_list,10)
page_number = request.GET.get('page')
posts = paginator.get_page(page_number)
return render(request,'main.html',
{
'average_price':average_price,
'min_price': min_price,
'max_price':max_price,
'posts':posts,
'paginator':paginator,
'filters':filters
})
UPDATE: it works when I set multiple filters, but when I set a single filter it still shows all the items
UPDATE 2: it works fine when I remove datetime__range=(start_date, end_date) from my object filter, does anyone know why ? seems to be a bug

Showing ads between posts django ? Trying to show ads after specific number of posts

I want to implement this thing, Like Facebook showing ads when we are scrolling posts, after 4th or 5th post we see some ads, how can I do that please can someone tell me? If any video link is available please share
Between two post like this
I am showing all the post by for loop, if any nested for loops available please share how can I loop two different model in single loop , showing 1st item of one model after 5th item of another model loop
**I am not using google ad sense, you can imagine its like I will create my own model , maybe same post model where is_ads=True be a field like that.
This is my post model-
class Post(models.Model):
postuuid = models.UUIDField(default=uuid.uuid4,unique=True,editable=False)
user = models.ForeignKey(settings.AUTH_USER_MODEL,on_delete=models.CASCADE, null=True)
title = models.CharField(max_length=150,blank=False)
text = models.TextField(null=True,blank=False)
image = models.ImageField(upload_to='post_images/',null=True,blank=True,default="")
created_at = models.DateTimeField(auto_now_add=True, null=True)
likes = models.ManyToManyField(User, blank=True, related_name="post_like")
tag= models.CharField(max_length=150,blank=True)
post_url=models.URLField(max_length=150,blank=True)
video = models.FileField(upload_to='post_videos/',null=True,blank=True,default="")
# community = models.ForeignKey(communities,on_delete=models.CASCADE)
def __str__(self):
return self.title
Looking at this, I think your best option would be to create a view function that creates two querysets and merges them together into a single one that you can pass to the context. It would be something like this:
# settings.py
AD_POST_FREQUENCY = 5 # Use this to control the frequency of ad posts appearing in the list of posts.
# views.py
from settings import AD_POST_FREQUENCY
def post_list(request):
ad_posts = Post.objects.filter(is_ad=True)
non_ad_posts = Post.objects.filter(is_ad=False)
posts = []
non_ad_post_section_count = 0
for i, post in enumerate(non_ad_posts):
posts.append(post)
if i % AD_POST_FREQUENCY == 0:
ad_post = ad_posts[non_ad_post_section_count]
posts.append(ad_post)
non_ad_post_section_count += 1
context = {
'posts': posts,
}
return render(request, 'your_template.html', context=context)
Using this approach, you would be able to essentially create a single list of posts to return to your template. Then you would only need to loop through the list in the template to display all the posts.
I haven't tried this myself but hopefully, it helps you.

Django how to annotate count nested forloop queryset dictionary

Good Day,
My Problem is:
my project depending on giving points to users who make any actions like (post, comment, favorite, likes ......).
So, in users list page i want to list all users and other data for each user (name, points, badges, ....)
to give the users points i have to count his posts, comments, likes, and so on.....
i tried several methods and ways but all is failed to get annotate or prefetch_related or select_related
Models.py
class Post(models.Model):
title = models.CharField(max_length=100)
content = models.TextField()
author = models.ForeignKey(User, on_delete=models.CASCADE, related_name='posts')
Views.py
def user_list(request):
users = User.objects.all()
template = 'user/users_list.html'
nested_posts = {}
for user in users:
posts = user.posts.all()
nested_posts[user, posts] = posts.count()
print("nested : ", nested_posts)
context = {
'users': users,
'user':user,
'posts': posts,
'nested_posts': nested_posts,}
return render(request, template, context)
when i print nested .. i found the count of every user' posts .. but how can i make it as variable to re-use in calculated field
QuerySet
nested : {(<User: Fareed>, <QuerySet [<Post: Senior Purchasing Specialist>]>): 1,
(<User: Hussein>, <QuerySet [<Post: Senior Software Development Engineer in Test>]>): 1,
(<User: Karima>, <QuerySet []>): 0,
(<User: Yahia>, <QuerySet []>): 0}
and i also tried :
GetUserID = User.objects.get(id=2)
var01 = GetUserID.posts.all().count()
but this was for one user with (id=2) .. and sure all users got the total posts of user(id=2) not for each of them.
and i also tried :
Posts_count_per_user = User.posts.annotate(posts_count=Count('posts'))
User_Score_of_posts = Posts_count_per_user.aggregate(posts_score=Count('posts_count') * 1000)
but i got this error:
'ReverseManyToOneDescriptor' object has no attribute 'annotate'
any suggestions please ...
Thanks in advance,
Have you tried this,
user_qs = User.objects.annotate(posts_count=Count('posts'))
# usage
for user_instance in user_qs:
print("post count: ", user_instance.posts_count)
print("post score: ", user_instance.posts_count * 1000)
Or you can annotate the post score in the DB level itself,
from django.db.models import F, Count
user_qs = User.objects.annotate(posts_count=Count('posts'), posts_score=F('posts_count') * 1000)

Django: Annotate and order by foreign key AVG ranking not filtering

I have a advertisement model and an review model, related by a Foreignkey('adfk') I am trying to get questions having heighest average ranking for their reviews.
For example:
AD1 - Average ranking with 4.
AD2 - Average ranking with 5.
In template the result should show AD2 first and AD1 second.
I am using this in my view but its not showing correctly. DB is postgres. IF I change ('-rating') to ('rating') it shows Ad1 first and Ad2 second, I mean atleast consider the order_by.
def adhome(request,tag_wise=None):
tags = Tag.objects.all()
cities= advertisement.objects.all().values('city').distinct()
view = request.GET.get('view')
if tag_wise:
questionlist=advertisement.objects.filter(tags__slug__in=[tag_wise])
else:
questionlist=advertisement.objects.all()
if view:
questionlist = questionlist.filter(city=view)
questionlist = questionlist.annotate(rating=Avg('adfk__rank')).order_by('-rating')
paginator = Paginator(questionlist,10) # Show 10 contacts per page
page = request.GET.get('page')
try:
contacts = paginator.page(page)
except PageNotAnInteger:
# If page is not an integer, deliver first page.
contacts = paginator.page(1)
except EmptyPage:
# If page is out of range (e.g. 9999), deliver last page of results.
contacts = paginator.page(paginator.num_pages)
return render(request,'classified/home.html',{'contacts':contacts,'tag_wise':tag_wise,'view':view,'tags':tags,'cities':cities,})
Looks like you want to control the order of NULL and not-NULL values.
There is a feature in PosgreSQL. However it doesn't supported by Django.
But you can do the following:
questionlist = questionlist.annotate(rating=Avg('adfk__rank'))
questionlist = questionlist.extra(
select={'adfk_is_null': 'adfk_id IS NULL'})
questionlist = questionlist.order_by('adfk_is_null', '-rating')
P.S. Looks like you are trying to aggregate a single value, so I recommend you to do F instead of Avg.

Django Forms - Relating Objects (Model Formsets?)

Say I have something like this:
class Product(models.Model)
name = models.CharField()
description models.TextField()
class Image(models.Model)
product = models.ForeignKey(Product, null=True, blank=True, related_name="images")
image = models.ImageField()
Suppose I'm in a form for creating a Product and in this form there is a section that allows you to upload images. These images are uploaded asynchronously. How can I have it so that:
On creation:
Product is created and images are related to it.
On editing:
Product is fetched, related Images are fetched. Product is edited, Images are edited.
Currently, I have a view that does both jobs of creating or editing Products. How can I accomplish the image related part of this product form? Model Formsets?
Edit:
#login_required
def create_or_edit_product(request, product_id=None):
# Redirect user if he has no account associated to him
try:
account = Account.objects.get(membership__user=request.user)
except:
login_url = reverse('login') + ('?next=%s') % request.path
return HttpResponseRedirect(login_url)
# Get product if product id passed
product_instance = None
if product_id is not None:
product_instance = get_object_or_404(product, id=product_id)
# Get related pictures if product exists. Get picture values (dictionary list, used for initial formset data) if pictures found.
pictures = product.pictures.all() if product_instance is not None else None
pictures_values = pictures.values() if pictures else []
PictureFormSet = formset_factory(PictureForm, formset=BasePictureFormSet, extra=0, can_delete=False, max_num=40)
if request.method == "POST":
product_form = productForm(request.POST, prefix='product', instance=product_instance)
picture_formset = PictureFormSet(request.POST, prefix='pictures', initial=pictures_values)
# If forms are valid
if product_form.is_valid() and picture_formset.is_valid():
try:
# Add account to product and save
product = product_form.save(commit=False)
if product_instance is None:
product.account = account
product.save()
# Remove picture-product relationships of current pictures
if pictures is not None:
pictures.update(product=None)
# Update each picture with the product and corresponding sort order. (The field 'id' is a picture object. The form converts the passed picture id to a picture object)
for index, form in enumerate(picture_formset):
picture = form.cleaned_data['id']
Picture.objects.filter(id=picture.id).update(product=product, sort_order=index)
except Exception, e:
# Rollback changes - something went wrong
transaction.rollback()
print "Transaction Rollback: %s" % e
else:
# Commit changes and redirect to product edit page
transaction.commit()
return HttpResponseRedirect('product_edit', product_id=product.id)
else:
product_form = productForm(prefix='product', instance=product_instance)
picture_formset = PictureFormSet(prefix='pictures')
# TODO: change add template to an add/edit one
return render_to_response('products/add.html', {
'product_form' : product_form,
'picture_formset' : picture_formset,
'pictures' : pictures,
},
context_instance=RequestContext(request))
I'm new to Python and Django but that is my somewhat working view. (I've only tried adding a new product so far)
The form that the user sees has thumbnails with hidden inputs containing each picture id. In the case that the form fails, I'd like to re display thumbnails along with the hidden inputs (which are already kept). To make that work, I'm guessing I'd have to query the picture ids, but the form is not valid so how would I go about doing that? (if that is even the right path to go about)
What do you think?
#Aamir Adnan is right that Model Formsets are an elegant way to deal with this.
Here's a full fledge example that is similar to what you need - http://stellarchariot.com/blog/2011/02/dynamically-add-form-to-formset-using-javascript-and-django/
As in the example, because the end user may need to add additional "images" (in your case) which are related to the product, you will need some javascript logic to handle the dynamic addition of forms so that the user can arbitrarily add and upload images on save.