How do I use ndb KeyProperty properly in this situation? - python

I have two models:
class User(ndb.Model):
email = ndb.StringProperty(required=True)
name = ndb.StringProperty(required=True)
class Post(ndb.Model):
created = ndb.DateTimeProperty(auto_now_add=True)
message = ndb.TextProperty(required=True)
user = ndb.KeyProperty(kind=User)
If I want to loop through some posts in the Post model. How I would access the 'name' field in the User model? For example:
for post in posts:
print post.message
print post.user.name
So I'm guessing the last line isn't correct. I tried searching and couldn't figure it out. And to add to that. Is there a way to access that info in a Jinja2 template?
And then finally, am I going about this wrong? Let's say there are 50 posts. And I have 1 query to get those 50 posts. Is accessing the 'name' field on the User model going to be an additional query for each time it loops? So 51 queries?? If so, is it better to not use KeyProperty and just store the User data with each post (even if I have more data like avatar, user_id, etc)?

If I recall correctly, a KeyProperty will return you a ndb.Key. Once you have the key, it is easy to get the model instance (key.get()). So, in your example, you would:
print post.user.get().name
As far as accessing it in a jinja template -- Sure, it'd just be something like:
{% for post in posts %}
{{ post.message }}
{{ post.user.get().name }}
{% endfor %}
And yes, this will interact with the datastore once for each key you have. If you'd rather, you can factor it out into one datastore interaction:
keys = [p.user for p in posts]
users = ndb.get_multi(keys)
user_posts = zip(users, posts)
And then in your jinja template:
{% for user, post in user_posts %}
{{ post.message }}
{{ user.name }}
{% endfor %}

Related

how to use django model object in django templates?

I am not able to use the Django model object in Django templates. I want to iterate using the model user in the template and then play with the ActivityPeriod(model) of that user. Please check my code for the clarity:
Here is my code:
views.py
from .models import User,ActivityPeriod
def call_response(request):
user = User.objects.all()
return render(request, "Test/list.html", {"users":user ,"activityperiod":ActivityPeriod})
Test/list.html
{% for user in users %}
'real_name': {{ user.real_name}}},
'activity_periods': {% with activity=activityperiod.objects.get(id =user) %}
{{ activity.start_time }}
{% endwith %}
{% endfor %}
But i am getting an error:
Could not parse the remainder: '(id' from 'activityperiod.objects.get(id'
What is the correct way? Can anyone please share it with me.
Django template don't understand the filter action of Model. This part shoud be in view.
activity=activityperiod.objects.get(id =user)
You should prepare your data and manipulate them before sending to template (a dictionary may help you). And remember that result of action "User.objects.all()" is a list.
views.py
def call_response(request):
user = User.objects.filter(user=request.user)
activityperiod = activityperiod.objects.get(user=user)
context={'user':user,'activityperiod':activityperiod}
return render(request, "Test/list.html",context})
Test/list.html
'real_name': {{ user.real_name}}
'activity_periods':{{ activityperiod.start_time }}
Your question suggests that you think you can a function in the templates like a normal function (ie activityperiod.objects.get(...)).
You can't, the templating system is not made like this (for security reasons amongst others).
You should do something like, in your models:
def call_response(request):
# ! first() not "all()" (if > 1 user, you'll have problem)!
user = User.objects.first()
activityperiod = activityperiod.objects.get(user=user)
return render(request, "Test/list.html",
{"users":user ,"activityperiod":activityperiod})

Hidden fields in Django template

I've passed 'Tango with Django' tutorial but still don't get one thing - why we need to use hidden fields in Django template.
For example, if I have a code
class CategoryForm(forms.ModelForm):
name = forms.CharField(max_length=128, help_text="Please enter the category name.")
views = forms.IntegerField(widget=forms.HiddenInput(), initial=0)
likes = forms.IntegerField(widget=forms.HiddenInput(), initial=0)
Ok, here I get, that views and likes won't be filled by user in form, so they are hidden.
But, in template, inside the form we have something like:
{% csrf_token %}
{% for hidden in form.hidden_fields %}
{{ hidden }}
{% endfor %}
{% for field in form.visible_fields %}
{{ field.errors }}
{{ field.help_text }}
{{ field }}
{% endfor %}
Why do we need 2-nd and 3-rd rows? And do we need them?
It's difficult to say exactly why, because the reasons could be pretty numerous.
But broadly speaking, it is probably so that those two fields can be modified or accessed client-side by javascript, and then also be submitted back to the server.
If you're not doing any of that, then you probably don't need them :) But you might have to worry about not overwriting those values when you save changes to the other fields.
Check the generated HTML. Even if hidden, those fields need to be present in the HTML, so that they get sent to the server on form submit. They are "hidden", not "absent".
And, indeed, quite often they are manipulated by the client-side javascript, as well, typically to put a value there.

Django SQL query duplicated n times

I have a book model and a rating model,
class Book(models.Model):
title = models.CharField(max_length=255)
slug = AutoSlugField(unique=True, populate_from='title')
description = models.TextField()
# more fields
class Rating(models.Model):
book = models.ForeignKey('library.Book')
score = models.DecimalField(max_digits=2, decimal_places=1)
the Query,
books = {'books': Book.objects.filter(pk__in=Rating.objects.all().order_by('-score'
).values_list('book__id', flat=True))[:10] }
template,
{% for i in books %}
{{ i.title }}, {{ i.rating_set.all.first.score }} <br/>
{% endfor %}
renders the model to the template, but the django debug toolbar shows as Duplicated n times where n is the number of objects in the list. when I use queryset caching, its normal.
whats going on behind, how can I fix this?
thanks.
Didn't test but you should definitely prefetch rating_set to not make additional database hit for each book to find their highest score:
rated_books = Rating.objects.all().order_by('-score').values_list('book', flat=True)
books = Book.objects.prefetch_related('rating_set').filter(pk__in=rated_books)[:10]
In the template, I also suspect .first and .all as they may cause an additional db hit. Besides, you don't need to call .first because we already know these rated books have at least one rating object.
{% for book in books %}
{{ book.title }}, {{ book.rating_set.all.0.score }} <br/>
{% endfor %}
Update: You need to use rating_set.all.0 instead of rating_set.0 to selec first rate
Read about select_related and prefetch_related.
Book.objects.filter(pk__in=Rating.objects.all().order_by('-score').values_list('book__id', flat=True)).preferch_related('rating_set')[:10]
In template You want to access to book rating {{ i.rating_set.all.0.score }}. Without select_related/prefetch_related Django in each row make new query. With prefetch_related Django made 1 query and fetch all ratings.
In Your case the problem may be in .first..

Django, revover a model with multi foreign key

I would like to show all favorites of connected user.
Each user can add his own favorites. I created a model to manage this action.
In this one, I have two foreign key. One for the user and the second for the "favorite".
models.py
class Favorite(models.Model):
user = models.ForeignKey(User)
drud = models.ForeignKey(Drud)
def __unicode__(self):
return self.drud.secure_key
On my view, I want to show all favorite Drud of connected user. I tried to do something like that:
views.py
favorite = Favorite.objects.filter(drud=[d.secure_key for d in Drud.objects.filter(user=request.user)])
But, that does work...
You can do:
fav_druds = request.user.favorite_set.values_list('drud', flat=True)
In the template you can do:
{% for drud in fav_druds %}
{{drud.id}}: {{drud.secure_key}}
{% endfor %}
Edit:
favs = request.user.favorite_set.all()
{% for fav in favs %}
{{fav.drud.id}}: {{fav.drud.secure_key}}
{% endfor %}

Django count specific items of a many-to-one relationship

I have a Django app where users post messages and other users can vote the answers up or down, very similar to SO. I'm having an issue trying to get the "thumbs up" and "thumbs down" counts from within the template and I'm hoping someone can help me. The PostVote is a many-to-one relationship with the Post class. Here is what my model looks like:
class Post(models.Model):
account = models.ForeignKey(Account)
message = models.CharField(max_length=1024)
timestamp = models.DateTimeField('post timestamp')
class PostVote(models.Model):
post = models.ForeignKey(Post)
account = models.ForeignKey(Account)
vote = models.CharField(max_length=16, choices=VOTE_CHOICES)
timestamp = models.DateTimeField('vote timestamp')
Here is how I'm getting my posts:
posts = Post.objects.all().order_by('-timestamp')[:10]
My template looks roughly like:
{% for post in posts %}
<div>Thumbs up count: {{ WHAT_HERE }}</div>
<div>Thumbs down count: {{ WHAT_HERE }}</div>
{% endfor %}
How can I get the counts in there? I'm sure it involves 'annotate' somehow, but I'm having a difficult time coming up with this one. Any help would be greatly appreciated!
You shouldn't really be doing logic in your templates. Add a couple count methods to your Post model:
class Post(models.Model):
account = models.ForeignKey(Account)
message = models.CharField(max_length=1024)
timestamp = models.DateTimeField('post timestamp')
def upvote_count(self):
return self.postvote_set.filter(vote=VOTE_CHOICES[0][0]).count()
def downvote_count(self):
return self.postvote_set.filter(vote=VOTE_CHOICES[1][0]).count()
Then use them in your template:
{% for post in posts %}
<div>Thumbs up count: {{ post.upvote_count }}</div>
<div>Thumbs down count: {{ post.downvote_count }}</div>
{% endfor %}

Categories