I am trying to display only objects, which are not older then 4 days. I know I can use a filter:
new = Books.objects.filter(pub_date__gt = datetime.now() - timedelta(days=4))
but I really want to use a modal method for exercise.
The method is defined in model Book and is called published_recetnly.
So my question is how to call a modal method in views.py?
This is my current code:
views.py
def index(request):
new = Books.objects.filter(pub_date__gt = datetime.now() - timedelta(days=4))
return render_to_response('books/index.html', {'new':new}, context_instance=RequestContext(request))
index.html
{% if book in new %}
{{ book.title }}
{% endif %}
models.py
class Book(models.Model)
pub_date = models.DateTimeField('date published')
def published_recently(self):
now = timezone.now()
return now - datetime.timedelta(days=4) <= self.pub_date <= now
Maybe you should use a manager in this case. It's more clear and you can use it to retrieve all published recently books.
from .managers import BookManager
class Book(models.Model)
pub_date = models.DateTimeField('date published')
objects = BookManager()
Set like this your managers file:
class BookManager(models.Manager):
def published_recently(self,):
return Books.objects.filter(pub_date__gt = datetime.now() - timedelta(days=4))
And now, you can filter more clearly in your views file.
Books.objects.published_recently()
Related
I am very new to Django so bare with me.
I have a model called Posts that looks like this:
class Post(models.Model):
author = models.ForeignKey(User,on_delete=CASCADE, related_name='posts')
content = models.TextField(max_length=127)
date = models.DateField(auto_now=True)
objects = PostManager()
def __str__(self):
return f"{self.author} : {self.content} -- ({self.date})"
And a model of Likes that looks like this:
class Like(models.Model):
post = models.ForeignKey(Post, on_delete=CASCADE, related_name='likes')
user = models.ForeignKey(User, on_delete= CASCADE, related_name='liked')
class Meta:
unique_together = ('post', 'user')
From the Like model I have a foreign key that points to my Post model.
If I want to access the amount of likes a post has I write this in the shell:
post1 = Post.objects.get(id =1)
post1.likes.all().count()
This is my view for rendering posts
#require_GET
def getposts(request):
posts = Post.objects.order_by('-date').all()
p = Paginator(posts, 10)
someposts = p.page(int(request.GET.get('page', 1)))
rendered_posts = render_to_string('network/posts.html', context={'page':someposts})
return JsonResponse({'posts': rendered_posts})
This is how post.html looks like
{% for post in page.object_list%}
<div class="post">
<ul class="post__head">
<li class="author">{{post.author.username}}</li>
<li class="date">{{post.date}}</li>
</ul>
<p class="content">{{post.content}}</p>
<div class="post__actions">
<span class="like__button"></span>
<p class="like__amount">{{post.likes.all().count()}}</p>
</div>
</div>
{% endfor %}
Now this line is what's giving me trouble:
<p class="like__amount">{{post.likes.all().count()}}</p>
I get an error that I can't use all() and count() in my template
I was wondering if there's a way of making a function that returns the like count when i call it.
Something in models.py like:
def getLikes():
return post.likes.all().count()
and if I call post1.getLikes() it returns the number of likes in post1?
Thank you and Please help!
Django's template engine is deliberately restricted to prevent people from writing business logic in a template: a template should contain rendering logic. Usually it is better to do this through the views.
We can make a queryset that contains the like_count with:
#require_GET
def getposts(request):
# add annotation ↓
posts = Post.objects.annotate(
like_count=Count('likes')
).order_by('-date')
p = Paginator(posts, 10)
someposts = p.page(int(request.GET.get('page', 1)))
rendered_posts = render_to_string('network/posts.html', context={'page':someposts})
return JsonResponse({'posts': rendered_posts})
The advantage of annotating, is that this means that the number of likes are determined in the same query where we load the Post objects, so it will slightly slow down that query, but it will only make one query, whereas using post.likes.count() will make an extra query per Post item.
Then we can render this as:
<p class="like__amount">{{ post.like_count }}</p>
You are about to achieve the goal:
When you work in templates you don't have to use () just call methods from templates like this:
{{post.likes.all.count}}
But if you want to abstract the logic to the model that 'is better' you can do the following:
class Post(models.Model):
author = models.ForeignKey(User,on_delete=CASCADE, related_name='posts')
content = models.TextField(max_length=127)
date = models.DateField(auto_now=True)
objects = PostManager()
def __str__(self):
return f"{self.author} : {self.content} -- ({self.date})"
def getLikes(self):
return self.likes.all().count()
Then you can call it in your template:
{{post.getLikes}}
That's it.
In Django, how can I sort the results of a method on my model?
class Flashcard(models.Model):
owner = models.ForeignKey(User, on_delete=models.CASCADE)
deck = models.ForeignKey(Deck, on_delete=models.CASCADE)
question = models.TextField()
answer = models.TextField()
difficulty = models.FloatField(default=2.5)
objects = FlashcardManager()
def __str__(self):
return self.question
class Profile(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE,related_name='profile')
bio = models.TextField(max_length=500,null=True, default='',blank=True)
def __str__(self):
return f'{self.user.username} Profile'
def avg_diff_user(self):
avg_diff = Flashcard.objects.filter(owner = self.user).aggregate(Avg('difficulty'))['difficulty__avg']
return avg_diff
So with avg_diff_user, I get each user's average difficulty rating. Which I can then use in my leaderboard template as follows:
<ol>
{% for user in leaderboard_list %}
<li>{{user.username}}: {{user.profile.avg_diff_user|floatformat:2}}</li>
{% endfor %}
</ol>
The results show, but it's not sorted - how can I sort by avg_diff_user? I've read many similar questions on SO, but to no avail. I've tried a different method on my model:
def avg_diff_sorted(self):
avg_diff_sorted = Flashcard.objects.all().annotate(get_avg_diff_user=Avg(Flashcard('difficulty'))['difficulty__avg'].order_by(get_avg_diff_user))
return avg_diff_sorted
Which I don't think is right and didn't return any results in my template. I also tried the following, as suggested in https://stackoverflow.com/a/930894/13290801, which didn't work for me:
def avg_diff_sorted(self):
avg_diff_sorted = sorted(Flashcard.objects.all(), key = lambda p: p.avg_diff)
return avg_diff_sorted
My views:
class LeaderboardView(ListView):
model = User
template_name = 'accounts/leaderboard.html'
context_object_name = 'leaderboard_list'
def get_queryset(self):
return self.model.objects.all()
something like:
leaderboard_list = User.objects.all().annotate(avg_score=Avg('flashcard__difficulty').order_by('-avg_score')
will sort you the users by their average score.
I don't use ListView that often by if you just used a standard view like:
def LeaderboardView(request):
leaderboard_list = ...
context = {'leaderboard_list':leaderboard_list}
return render(request, 'accounts/leaderboard.html', context)
In your html you could do the same:
{% for user in leaderboard_list %}
...
{% endfor %}
I have the following problem. I need to get the information of the question based on the questionitem.question_id
I have the following files.
# models.py
class Question(models.Model):
qtype_id = models.ForeignKey(QuestionType, on_delete=models.CASCADE )
q_discription = models.CharField(max_length=150)
def __iter__(self):
return self.pk
class QuestionItem(models.Model):
exam_id = models.ForeignKey(Exam, on_delete=models.CASCADE)
question_id = models.ForeignKey(Question, on_delete=models.CASCADE)
question_pontuation = models.FloatField(default=0.0)
def __iter__(self):
return self.pk
# views.py
def ExamDetail(request, exam_id):
exam = get_object_or_404(Exam, pk=exam_id)
questionitems = QuestionItem.objects.filter(exam_id= exam_id).values_list('id', flat=True)
questions = Question.objects.filter(pk__in=questionitems)
context = {
'exam': exam,
'questions': questions,
'questionitem': questionitems,
}
return render(request, 'evaluation/exam_detail.html' , context)
And now on exam_detail.html i have something like this:
{% for questionitem in exam.questionitem_set.all %}
Question {{questionitem.id}}</a>: {{question.q_discription}}
{% endfor%}
but nothing shows, and i need to show the description of the question in questionitem.question_id, and i cant change the models.py.
Probably, you have problem in the way, you are accessing queryset in loop. Change code to :-
{% for questionitem in exam.questionitem_set.all %}
Question {{questionitem.id}}</a>: {{questionitem.question_id.q_discription}}
{% endfor%}
q_discription is the part of Question class, which is accessible using question_id on questionitem instance, that you receive in the loop.
I have a reddit-like website where users post links and those links can be commented on. I want to display the number of comments under each link before being taken to the comment page for that link. What is the most query efficient way to do this in django that doesn't slow down the rendering of my site? I assume I'll have to go through a for loop of each link on the page to count the number of posts for each one and populate a list of some sort with the number returned from the .count() of each queryset? Here's what I have:
class Post(models.Model):
newlinktag = models.ForeignKey('NewLink', null=False)
childtag = models.ForeignKey('Post', blank=True, null=True)
postcontent = models.CharField(max_length=1024)
def __unicode__(self):
return self.postcontent
class NewLink(models.Model):
posttag = models.ForeignKey('PageInfo') #the page each link belongs to
linkcomment = models.CharField(max_length=512)
postlinkdate = models.DateTimeField(auto_now_add=True) #submission datestamp.
url = models.URLField(max_length = 1024)
linkowner = models.ForeignKey(User, null=True, blank=True)
def __unicode__(self):
return self.url
Jape gave you a good answer, but it is always more efficient to preform counting in the database rather than in python loops.
views.py
from django.db.models import Count
def view(request):
# Calculate the counts at the same time we fetch the NewLink(s)
links = NewLink.objects.annotate(post_count=Count('post_set'))
return render(request, 'template.html', {'links': links})
html
{% for link in links %}
{{ link.post_count }}
{% endfor %}
In your model, I would create a cached_property and then when you run the for loop in your template, call the property to get the count.
For example,
models.py:
class NewLink(models.Model):
posttag = models.ForeignKey('PageInfo') #the page each link belongs to
linkcomment = models.CharField(max_length=512)
postlinkdate = models.DateTimeField(auto_now_add=True) #submission datestamp.
url = models.URLField(max_length = 1024)
linkowner = models.ForeignKey(User, null=True, blank=True)
def __unicode__(self):
return self.url
# Might also want to flush this after a post_save method in your signals
#cached_property
def linkcomment_count(self):
return self.linkcomment.count()
views.py:
def view(request):
# Could do a 'select_related' relationship to save hits on the database
comments = NewLink.objects.all()
return render(request, 'template.html', {'comments': comments})
html:
{% for link in comments %}
{{ link.linkcomment_count }}
{% endfor %}
Did I understand your question correctly?
Hi bit of a beginner question about using django's modelchoicefield in a form I'm building.
I just need get django to display a drop down list of ingredients in a form. I've gotten to the point where the page renders but the form does not, I was getting errors before so I am kind of perplexed at the moment. I was hoping for some guidance.
Using python 2.7.6 and django 1.6.2. If I left anything out let me know.
Thanks!
Code is below:
views:
args = {}
#add csrf sercurity
args.update(csrf(request))
args['form'] = form
return render_to_response('newMeal.html', args)
form:
from django import forms
from models import meals, ingredients, recipe
class mealForm(forms.ModelForm):
breakfast = forms.ModelChoiceField(queryset=recipe.objects.all())
# Lunch = forms.ModelChoiceField(queryset=recipe.objects.all())
# Dinner = forms.ModelChoiceField(queryset=recipe.objects.all())
class Meta:
model = meals
fields = ('Breakfast','Lunch','Dinner','servingDate')
class recipeForm(forms.ModelForm):
class Meta:
model = recipe
fields = ('Name', 'Directions')
template:
{% extends "base.html" %}
{% block content %}
<p>New Meals go here!</p>
<form action="/meals/newmeal/" method="post">{% csrf_token %}
<table class="selection">
{{form.as_table}}
<tr><td colspan="2"><input type="submit" name="submit" value="Add Meal"></td></tr>
</table>
</form>
{% endblock %}
Model;
from django.db import models
import datetime
Create your models here.
class recipe(models.Model):
Name = models.CharField(max_length=200)
Directions = models.TextField()
pub_date = models.DateTimeField(auto_now_add = True)
def __unicode__(self):
return (self.id, self.Name)
class ingredients(models.Model):
Name = models.CharField(max_length=200)
Quantity = models.IntegerField(default=0)
Units = models.CharField(max_length=10)
Recipe = models.ForeignKey(recipe)
def __unicode__(self):
return self.Name
class meals(models.Model):
Breakfast = models.CharField(max_length=200)
Lunch = models.CharField(max_length=200)
Dinner = models.CharField(max_length=200)
servingDate = models.DateTimeField('date published')
did you import the mealForm:
some thing like :from app.forms import mealForm
form is a function. so try:
args['form'] = mealForm()
Note: don't use render_to_response. it is old use render instead(so don't even need csrf)::
from django.shortcuts import render
def...(request):
....
return render(request,'newMeal.html', {'form': mealForm()})