I'm making a very basic poll app. It's similar to the one in the Django tutorial but I chose to break out the vote counting aspect into its own model (the tutorial just adds a vote count field alongside each answer). Here's my models:
class PollQuestion(models.Model):
question = models.CharField(max_length=75)
class PollAnswer(models.Model):
poll = models.ForeignKey('PollQuestion')
answer = models.CharField(max_length=75)
class PollVote(models.Model):
poll = models.ForeignKey('PollQuestion')
answer = models.ForeignKey('PollAnswer')
date_voted = models.DateTimeField(auto_now_add=True)
user_ip = models.CharField(max_length=75)
I'm trying to show all of the vote counts for a given poll. Here's my view code:
from django.db.models import Count
poll_votes = PollVote.objects.select_related('PollAnswer').filter(poll=poll_id).annotate(num_votes=Count('answer__id'))
When I output the results of this query I just get a single row per vote (eg I see about 40 'answers' for my poll, each one representing a vote for one of the 5 actual PollAnswers). If I look at the queries Django makes, it runs something like this for every vote in the poll:
SELECT `poll_answers`.`id`, `poll_answers`.`poll_id`, `poll_answers`.`answer`
FROM `poll_answers`
WHERE `poll_answers`.`id` = 101
Can anyone poke me in the right direction here? I get the feeling this should be easy.
EDIT: here's my template code, for completeness.
<ul>
{% for vote in votes %}
{{ vote.answer }} ({{ votes.num_votes }})<br />
{% endfor %}
</ul>
Try:
poll_votes = PollVote.objects.filter(poll=poll_id).annotate(num_votes=Count('answer__id'))
or:
poll_votes = PollVote.objects.values('poll', 'answer__answer').filter(poll=poll_id).annotate(num_votes=Count('answer__id'))
Relevant docs:
Django offical docs
Never mind, fixed it myself after finding a tutorial which uses the same sort of models as me.
Essentially the fix was in the view:
p = get_object_or_404(PollQuestion, pk=poll_id)
choices = p.pollanswer_set.all()
And in the template:
{% for choice in choices %}
<p class="resultsList">{{choice.answer}} - {{choice.pollvote_set.count}}</p>
{% endfor %}
Related
I'm having problems using {% regroup %} Django template tag.
A brief summary: I succeed listing all my orders with their products ordered in the same template. So all seems to works fine doing the following:
Create the order
Create products ordered
Assing those products to the order
Display the daily orders with their products in the same template (filtered by date also) in dailyorders.html
Here my codes and only I'll show the code which allows me to display the orders and modify one if I want (where I have the problem)
models:
class Productsordered (models.Model):
item = models.ForeignKey(Itemlist, on_delete=models.CASCADE)
order = models.ForeignKey(Orders, on_delete=models.CASCADE)
quantity = models.IntegerField(default=1)
def __str__(self):
return f"{self.quant} - {self.item.nombre_producto}"
class Orders(models.Model):
date = models.DateField(default=date.today)
client_name = models.CharField(max_length=30) (just name, not client id)
def __str__(self):
return self.client_name
class Itemlist(models.Model):
id = models.SlugField(primary_key = True, max_length =30)
name_item = models.CharField(max_length=30)
price = models.IntegerField()
Just daily orders and modify order views:
def show_daily_orders(request,**kwargs):
daily_products_ordered =
Productsordered.objects.filter(order__date__day=date.today().day,
order__date__month=date.today().month,
order__date__year=date.today().year)
return render(request,'dailyorders.html',{'daily_products_ordered':daily_products_ordered})
def add_products_ordered (request,pk_order,pk_item):
products_ordered=Productsordered.objects.filter(order=pk_order)
get_order=Orders.objects.get(id=pk_pedido)
list= Itemlist.objects.all()
#ask if the product is already there, if not create one
try:
product = products_ordered.get(item=pk_item)
product.quantity += 1
product.save()
except:
newproduct = Itemlist.objects.get(id=pk_item)
newproduct = Productsordered.objects.create(item=newproduct, order=get_order)
product.save()
return render(request,'modify.html',{'list':list,'products_ordered':products_ordered,'order':order})
dailyorders.html would be something like:
{% regroup daily_products_ordered by order as order_list %}
{% for order in order_list %}
<h2> {{order.grouper}}
{% for product in order.list %}
<li> {{product.quantity}} - {{ product.item.name_item }}</li>
<a class = 'btn' href="{% url 'go_to_modify_order' pk=order.grouper.id %}">Modify order</a>
{% endfor %}
{% endfor %}
modify.html
{% for prod in list %}
<a class="btn" href="{% url 'go_to_modify_order' pk_item=prod.id pk_order=order.id %}">{{prod.name_item}}</a>
{% endfor %}
<a class="btn" href="{% url 'go_to_orders' pk_item=prod.id pk_pedido=order.id %}"> Finish modifying order </a>
# Also there would be the choice to delete an added product, but no matter for the problem.
The problem is that when I try to modify an order and add a new product (simply clicking the buttons and adding them to the cart) when I return to the template where I get all orders, regroup counts this new product as if it were from another order. For example:
Laura (order_id=1):
Product A
Product B
Carlos (order_id=2):
Product X
Laura (order_id=1):
Product C (this would be the product added when modify order!)
In order to see what happened, I went to the admin section and noticed that this new product is even added to the order I modified (Product C belonging to order_id=1 for example). So, it seems to be a problem when passing from the modify template to the daily orders, or something else I haven't noticed yet.
I'd appreciate help in this topic.
Thank you all
I've already done!
Seems to be that regroup only works when you have your dictionary order by the attribute you want to regroup! That's because I was getting the orders separately (see the example Laura (order_id=1) twice! see django documentation about regroup built-in template
My code looks like this:
models.py
class Tag(models.Model):
name = models.CharField(max_length=42)
class Post(models.Model):
user = models.ForeignKey(User, related_name='post')
#...various fields...
tags = models.ManyToManyField(Tag, null=True)
views.py
posts = Post.objects.all().values('id', 'user', 'title')
tags_dict = {}
for post in posts: # Iteration? Why?
p = Post.objects.get(pk=[post['id']]) # one extra query? Why?
tags_dict[post['id']] = p.tags.all()
How am I supposed to create a dictionary with tags for each Post object with minimum set of queries? Is it possible to avoid iterating, too?
Yes you will need a loop. But you can save one extra query in each iteration, you don't need to get post object to get all its tags. You can directly query on Tag model to get tags related to post id:
for post in posts:
tags_dict[post['id']] = Tag.objects.filter(post__id=post['id'])
Or use Dict Comprehension for efficiency:
tags_dict = {post['id']: Tag.objects.filter(post__id=post['id']) for post in posts}
If you have Django version >= 1.4 and don't really need a dictionary, but need to cut down the count of queries, you can use this method like this:
posts = Post.objects.all().only('id', 'user', 'title').prefetch_related('tags')
It seems to execute only 2 queries (one for Post and another for Tag with INNER JOIN).
And then you can access post.tags.all without extra queries, because tags was already prefetched.
{% for post in posts %}
{% for tag in post.tags.all %}
{{ tag.name }}
{% endfor %}
{% endfor %}
I'm working on a project where users can vote on collections of books. My models look like this:
class Collection(models.Model):
id = models.AutoField(primary_key=True)
name = models.CharField(max_length=31)
class Book(models.Model):
id = models.AutoField(primary_key=True)
name = models.CharField(max_length=255)
collection = models.ForeignKey(Collection)
vote_choices = ((-1,'Bad'),
(0, 'Not good, Not bad'),
(1,'Good'))
class Vote(models.Model):
id = models.AutoField(primary_key=True)
user = models.ForeignKey(User, related_name='votes')
book = models.ForeignKey(Book)
vote = models.IntegerField(choices=vote_choices)
I need to display a view for the users where they can see all the books in a given collection and vote for those books at once, since this is much more user-friendly than looping over everybook to vote. So I need a formset where every form will be: a book and its vote.
Initially I thought this was a good opportunity to use formsets (I have never used them before, but I'm used to work with forms, modelforms), but I'm running into difficulties on how to create this formset.
I would like to use generic class based views, since, in theory, I could easily create/update all votes easily, but I can't figure out how to do this.
Right now, I don't have a direct relation between the Collection model and the User (from django.contrib.auth.model) model, but I don't mind adding one if this makes things easier. I don't really need one, since every User is linked to a Book through a Vote and every Book is linked to a Collection.
I'm not sure whether I should use formsets or just regular modelforms.
How would you do it? Thanks in advance for any help.
I would recommend not to use django forms for this case. There are some more convenient ways.
First way. Using pure jQuery
Extend your book class:
class Book(models.Model):
id = models.AutoField(primary_key=True)
name = models.CharField(max_length=255)
collection = models.ForeignKey(Collection, related_name='books')
def get_vote(self, user):
vote = Vote.objects.filter(book=self, user=user)
if vote:
return vote.vote
else:
return ''
Your views (you can rewrite it on CBV if you want):
def list_collection(request, collection_id):
collection = Collection.objects.get(pk=id)
user = request.user
books = []
for item in collection.books.all():
book = {
'id': item.pk,
'name': item.name,
'vote': item.get_vote(user)
}
books.append(book)
return render_to_response('colleciton.html', {'books': books})
def set_vote(request):
id = request.GET('id')
vote = int(request.GET('vote'))
user = request.user
book = Book.objects.get(pk=id)
vote = Vote()
vote.user = user
vote.vote = vote
vote.book = book
vote.save()
return HttpResponse()
Your urls:
url(r'^list-collection/', 'list_collection', name='list_collection'),
url(r'^set-vote/', 'set_vote', name='set_vote'),
Your django template:
{% extends 'base.html' %}
{% block content %}
{% for book in collection.books.all %}
<div class="book" data-id="{{ book.id }}">
<div class="book-name">{{ book.name]}</div>
<div class="book-vote" {% if not book.get_vote %}style="display:none"{% endif %}>book.get_vote</div>
<div class="voting" {% if book.get_vote %}style="display:none"{% endif %}>
<button class="vote-up"></button>
<button class="vote-down"></button>
</div>
</div>
{% endfor %}
{% endblock %}
And the javascript is like
$(document).ready(function(){
$('.vote-up, .vote-down').on('click', function(e){
var id, vote, t, book_el;
e.preventDefault();
t = $(e.currentTarget);
book_el = t.parents().parents()
id = book_el.attr('data-id');
if t.hasClass('vote-up'){
vote = 1;
} else {
vote = -1;
}
book_el.find('.book-vote').show();
book_el.find('.book-vote').text(vote);
book_el.find('voting').hide();
$.get("/set-vote", {vote: vote, id: id});
});
});
Second way. Using javascript frameworks like Backbone.js
It makes the whole process much easier. Especially if you plan to add some other features to your app.
From the other hand Backbone requires some time to learn it.
Read the docs, look at the tutorial app TodoMVC or other tutorials.
Maybe you'll find it more appropriate for your task.
I am very new to Django and at the end of my rope and really need some help.
I do not know how to use a "class based view" and change the incoming datetimefield from my MySQL database into a Time Zone Supported entry that it seems to need. The database stores it in UTC and my system is on PST.
I am getting this error:
DateTimeField received a naive datetime (2012-09-01 00:00:00) while time zone support is active
On my MonthArchiveView, DayArchiveView, DateDetailView 's only. For some reason my ArchiveIndexView, YearArchiveView class based views work ok.
Here is my model:
class blogpost(models.Model):
blog_title = models.CharField(max_length=200)
blog_slug = models.SlugField(unique_for_date='blog_pub_date', max_length=200)
blog_content = models.TextField()
blog_pub_date = models.DateTimeField(default=datetime.now())
blog_category = models.ForeignKey('blogcategory')
Here is one of my Views:
class ThoughtsDetailView(DateDetailView):
template_name='thoughts/thoughts_detail.html'
queryset = blogpost.objects.all()
date_field = 'blog_pub_date'
slug_field = 'blog_slug'
context_object_name = 'thoughts_detail'
month_format = '%m'
allow_future = 'true'
Here is an example template:
{% block content-inner-left %}
<h1>{{ thoughts_detail.blog_title }}</h1>
<div id="blogpost">
<p class="blogsmalldate">[ Posted on {{ thoughts_detail.blog_pub_date|date:"l, F dS, Y" }}, {{ thoughts_detail.blog_pub_time|date:"g:i a" }} ]</p>
<br />
<p>{{ thoughts_detail.blog_content|safe|linebreaks }}</p>
</div>
{% endblock content-inner-left %}
Can someone help me understand how to fix my Day Detail View so that it stays as a Class Based View and then I can probably figure out the others. I even tried to use PYTZ but don't understand enough how to change the class based view to use it. Thank you....
The problem is not in the view, but in the fact that the dates are stored in the database without a timezone information, while Django is set up to support timezones. If you don't need timezone support, simply set USE_TZ = False in the settings.py; if you do, make sure that the database stores the dates with the timezone information. More details on that can be found at https://docs.djangoproject.com/en/1.4/topics/i18n/timezones/#naive-and-aware-datetime-objects
I have setup like so (changed for simplicity)
class Author(models.Model)
name = models.CharField(max_length=100)
...
class Document(models.Model):
title = models.CharField(max_length=200)
content - models.TextField()
author = models.ForeignKey("Author", related_name="documents")
date_published = models.DateTimeField()
categories = models.ManyToManyField("Category")
class Category(models.Model):
name = models.CharField(max_length=100)
I'm pulling in the Author records but I only want to pull in related document records for each author that match specific criteria -- say, date_published and category.
I know the easy way to do this would be to pull in the records as a list of dictionaries using Author.objects.values(), looping through each record and running:
author['documents']=Document.objects.filter(categories__in=[category_list], date_published__year=year)`
However, this is being generated for django-piston, and it seems to be happiest (particularly if you're defining your own fields!) if you return a QuerySet object.
Part of this may be because I made changes to the base django-piston code. Basically, the current version of the code here overwrites the fields value. I changed this code so that I could change the fields value for a Handler based on the request (so I could provide more details if the request was for a specific resource).
So I guess my question is three-fold:
Is there a way to filter or somehow limit the subrecords of a record (i.e. filter documents for each author.documents)
If not, what is a functional way of doing this that also works with django-piston?
Is there some easier, better way to do what I'm trying to do (display all the authors without their documents if an id is not given, but displaying the sub-records if filtering to just one author)?
Clarification
Okay, just to be clear, here is the pseudocode that I want:
def perhaps_impossible_view(request, categories=None, year=None):
authors = Author.objects.all()
authors.something_magical_happens_to_documents(category__in=[categories], date_published__year=year)
return render_to_response('blar.html', locals(), RequestContext(request))
So that if I were to put it in a template, this would work without any modifications:
{% for a in authors %}
{% for d in authors.documents.all %}
{{ d.title }} is almost certainly in one of these categories: {{ categories }} and was absolutely published in {{ year }}. If not, .something_magical_happens_to_documents didn't work.
{% endfor %}
{% endfor %}
something_magical_happens_to_documents has to run and actually change the contents of documents for each author record. It seems like this should be possible, but perhaps it isn't?
Edited... or better... replaced! :)
That's true the authors without document matching won't be in the queryset so you will have to add them back after (I couldn't find a better way but maybe someone knows how to not remove them without using raw sql).
You get the full documents count of the authors because you don't use the queryset to get the document counts:
qryset = Author.objects.all()
qryset = qryset.filter(documents__categories__name__in = category_list).distinct()
qryset = qryset.filter(documents__date_published__year=year)
print(qryset) gives [<Author: Author object>] (if only 1 author matched all categories) and qryset[0].documents.count() will return only the number of documents matched (not all documents from the author - 2 in the case I tested and the author had 4 but only 2 matching all conditions).
If you use dict (.values()) instead of querysets in the steps above, you can't do that (I think) because dict won't have a documents field so:
qryset_dict = Author.objects.values()
qryset_dict = qryset_dict.filter(documents__categories__name__in = category_list).distinct().values()
qryset_dict = qryset_dict.filter(documents__date_published__year=year).values()
when you issue qryset_dict[0].documents.count() you receive an error:
AttributeError: 'dict' object has no attribute 'documents'
Now to add the filtered authors back you can do:
res = []
for a in Author.objects.all():
if a in qryset:
res.append([a,a.documents.count()])
else:
res.append([a,0])
and res will be a list with <authors> in 1st column and count of documents matching in 2nd column.
I know this is far from perfect... but if you are interested only in the count() of matching documents per author, I think you could find a better way using django aggregation and annotation or possibly make it with raw sql using a left join from authors to documents.
EDIT after Clarification in Question:
def possible_view(request, categories=None, year=None):
# you will pass these as parmeters of course
category_list = ['c2', 'c3']
year = 2010
qryset = Document.objects.filter(categories__name__in = category_list).distinct()
qryset = qryset.filter(date_published__year=year)
authors = Author.objects.all()
return render_to_response('blar.html', { 'result': qryset, 'authors': authors, 'categories': category_list, 'year': year }, RequestContext(request))
Template blar.html:
{% for a in authors %}
<b>{{a.name}}</b><br />
{% for d in result %}
{% if d.author.name == a.name %}
{{ d.title }} is almost certainly in one of these categories: {{ categories }} and was absolutely published in {{ year }}. If not, .something_magical_happens_to_documents didn't work.<br />
{% endif %}
{% endfor %}<br />
{% endfor %}
This will give you something not very pretty but with all authors and below each one, the list of their documents that fall within one of the category_list (OR condition, for AND, you need to filter the query for each category instead of using __in).
If the author has no document in the category_list, it wil be listed without documents below him.
Something like:
aut1
tit2 is almost certainly in one of these categories: ['c2', 'c3'] and was absolutely published in 2010. If not, .something_magical_happens_to_documents didn't work.
tit1 is almost certainly in one of these categories: ['c2', 'c3'] and was absolutely published in 2010. If not, .something_magical_happens_to_documents didn't work.
aut2
tit3 is almost certainly in one of these categories: ['c2', 'c3'] and was absolutely published in 2010. If not, .something_magical_happens_to_documents didn't work.
aut3