Django ManyToMany Template Questions - python

Good Morning All,
I've been a PHP programmer for quite some time, but I've felt the need to move more towards the Python direction and what's better than playing around with Django.
While in the process, I'm come to a stopping point where I know there is an easy solution, but I'm just missing it - How do I display manytomany relationships in a Django Template?
My Django Model: (most of the fields have been removed)
class Category(models.Model):
name = models.CharField(max_length=125)
slug = models.SlugField()
categories = models.ManyToManyField(Category, blank=True, null=True)
class Recipe(models.Model):
title = models.CharField('Title', max_length=250)
slug = models.SlugField()
class Photo(models.Model):
recipe = models.ForeignKey(Recipe)
image = models.ImageField(upload_to="images/recipes", blank=True)
So, there is the basic models I'm using in my application called "recipes."
With that said, there are two questions I'm looking for answers on:
How would I go about displaying the categories for a recipe on it's details page?
How would I go about displaying the image for the recipe on it's details page?
If I go into the Python shell, and input the following, I do get a result:
>>> photos = Photo.objects.filter(recipe=1)
>>> photos
[<Photo: Awesome Pasta>]
>>> for photo in photos:
... print "Photo: %s" % photo.logo
...
Photo: images/recipes/2550298482_46729d51af__.jpg
But when I try something like the following in my template, I get an error saying "Invalid block tag: 'photo.image'."
{% for photo in photos %}
{% photo.image %}
{% endfor %}
Although, even if that did work, the ID is still hard coded into the view, how would you go about having this dynamic for each recipe?
Details Page View.py snippet:
def details(request, slug='0'):
p = get_object_or_404(Recipe, slug=slug)
photos = Photo.objects.filter(recipe=1)
return render_to_response('recipes/recipes_detail.html', {'p': p, 'photos': photos})
Thanks in advance for the help and understanding for what is probably a very simple question to all of you!
UPDATE: When removing the additional fields in the models, I forgot the categories field for the Recipes model.

From what I can see, I think you've got a small syntax error:
{% photo.image %}
should instead be:
{{ photo.image }}
The {% %} notation is used for django template tags. Variables, on the other hand, are expressed with the {{ }} notation.
To make it dynamic, you can take advantage of the fact that your Photo model has a foreign key to Recipe. This means that there will be a reverse relation from the Recipe instance you've loaded using the slug back to the set of photos:
def details(request, slug='0'):
p = get_object_or_404(Recipe, slug=slug)
photos = p.photo_set.all()
Hopefully that will work for you. Glad to see you're enjoying working with Django!

Related

Django how to add query results foreign key model to queryset

How can I get the Author model objects into a queryset through the foreign key of B model? I would like to use the Author objects in my template.
#Models.py
class Book(models.Model):
name = models.CharField(max_length=5)
class Author(models.Model):
# lots of fields
class B(models.Model):
author = models.ForeignKey(Author)
book = models.ForeignKey(Book)
selected_book = "ABC"
book = Book.objects.get(name=selected_book)
original_queryset = B.objects.filter(name=book)
for i in original_queryset:
print(i.author) # returns what i want
queryset = # get all i.author objects somehow
return render(request,"template.html", {"queryset": queryset}
Remove the for loop. then add the following line after the original_queryset = B.objects.filter(name=book) line->
queryset = original_queryset.author
If this doesn't work then let me know. I hope I can help you.
You can rename your B model's author field to authors, that makes your code more meaningful because ForeignKey field can store many data. In your code, you have made your B model's author field as a ForeignKey field, which means books can have multiple authors. You can see the django documentation of ForeignKey reference.
If you change as I told you then don't forget to run migration(python manage.py makemigrations and python manage.py migrate command in your cmd) and change the line queryset = original_queryset.author to queryset = original_queryset.authors
Another approach
You can pass the original_queryset in the context of the template,(i.e: {'queryset':original_queryset }) then in your template.html, you can add tages like this:
{% for book in queryset %}
<ul>
{% for author in book.author %}
<li>author.name</li>
....
{% endfor %}
</ul>
{% endfor %}
By doing these, you can place your books and authors in your template nicely. If it still shows error, message me, I hope I can help you.

Django aggregation multiple filters

I have a Django project that allows users to create their own Projects and add multiple Skills to each Project.
I am trying to write a view that will allow me to display the name of each Skill as well as the Count of that Skill for all of that particular user Profile's Projects that are published.
For example, if a user has three projects where they've added the Skill "HTML" I'd like to show HTML (3) on their Profile, and so on.
Below is my current code which mostly works however it displays the count for that Skill from ALL user projects and not the specific user whose profile is being viewed.
Models:
#accounts/models.py
class Profile(models.Model):
#...
skills = models.ManyToManyField('skills.skill')
#projects/models.py
class Project(models.Model):
user = models.ForeignKey(User)
#...
published = models.BooleanField(default=False)
skills = models.ManyToManyField('skills.skill')
#...
#skills/models.py
class Skill(models.Model):
name = models.CharField(max_length=100)
Views:
#accounts/views.py
def profile(request, username):
user = get_object_or_404(User, username=username)
skill_counts = Skill.objects.annotate(num_projects=Count('project')).filter(project__user_id=user.id).order_by('-num_projects')[:10]
return render(request, 'accounts/profile.html', {
'user': user,
'skill_counts': skill_counts,
})
Template:
#accounts/templates/accounts/profile.html
{% for skill in skill_counts %}
<div class="skill-container">
<div class="label-skill">{{ skill.name }}</div> <div class="label-skill-count">
{{skill.project_set.count}}</div>
</div>
{% endfor %}
Any help here would be much appreciated.
Thanks in advance.
Note that I'm voting to close this as this question has been answered here.
Basically you need to use django's conditional expressions Case and When.
Here is a quick example of how this could look:
from django.db.models import Count, Case, When
# ...
Skill.objects.annotate(
num_porjects=Count(Case(
When(project__user_id=user.id, then=1),
output_field=CharField(),
))
)

django like integration not working

I want to integrate like model in my app. Then this Q/A really helped me to start over it.
class Like(models.Model):
user = models.ManyToManyField(User, related_name='likes')
article = models.ForeignKey(Article)
created = models.DateTimeField(auto_now_add=True)
total_likes = models.IntegerField(default=0)
But later i found one problem
Cannot resolve keyword 'slug' into field. Choices are: content, creation_date, id, like, title, user, user_id
Well now started question what is really slug field here ? in name="{{ article_slug }}". Well nothing printed in html page [name=""] And then i found
slug = request.POST.get('slug', None)
article = get_object_or_404(Article, slug=slug)
liked, created = Like.objects.create(article=article)
this code is causing above issue.So i changed the code to slug=slug ==> content=slug then this time i am getting different error..
<p>No Article matches the given query.</p>
Ah .. this time i got it.. Now i changed to name="{{ id }}" .. hm nothing happened same error..
silly me.. definitely i am missing something really very simple. Anybody got it?
get_object_or_404 works with a specific, unique field that is specified on the model.
Therefore ensure that the slug field is specified on your Article model class. Example:
class Article(models.Model):
slug = models.SlugField(max_length=50)
# remaining fields

django - get latest object with model method in template

I have a model with method.
class Book(models.Model):
title = models.TextField()
class Review(models.Model):
content = models.TextField()
book = models.ForeignKey(Book,related_name="book_reviews")
def lastreview(self):
return self.objects.order_by('-id')[:1]
but once I use this method in template
{{book.book_reviews.lastreview.content}}
it is showing nothing..
what am i missing here?
I believe you are trying to retrieve the latest review of a book (which was not clear from your question). The related reviews for a book can be accessed by book.book_reviews. It is a QuerySet rather than a single Review object, hence {{ book.book_reviews.lastreview }} will fail.
I would recommend moving the lastreview method directly under Book, like:
class Book(models.Model):
title = models.TextField()
def lastreview(self):
try:
return self.book_reviews.order_by('-id')[0]
except IndexError:
return None
Now you can access the latest review of a book in your template by
{{ book.lastreview.content }}
Note: I recommend adding a DateTimeField to Review, as finding the latest review based on id can be misleading. For e.g., someone can edit an old review and it will not be shown as the latest.

Trouble handling generic relationship in django

I want to model a situation and I´m having real trouble handling it. The domain is like this: There are Posts, and every post has to be associated one to one with a MediaContent. MediaContent can be a picture or a video (for now, maybe music later). So, what I have is:
mediacontents/models.py
class MediaContent(models.Model):
uploader = models.ForeignKey(User)
title = models.CharField(max_length=100)
created = models.DateTimeField(auto_now_add=True)
def draw_item(self):
pass
class Meta:
abstract = True
class Picture(MediaContent):
picture = models.ImageField(upload_to='pictures')
class Video(MediaContent):
identifier = models.CharField(max_length=30) #youtube id
posts/models.py
class Post(models.Model):
...
# link to MediaContent
content_type = models.ForeignKey(ContentType)
object_id = models.PositiveIntegerField()
media_content = generic.GenericForeignKey('content_type', 'object_id')
What i eventually want to do, is beeing able to call methods like:
post1.media_content.draw_item()
>> <iframe src="youtube.com" ...>
post2.media_content.draw_item()
>> <img src="..."/>
Is this the correct aproach, does it work? Can the template be agnostic of the object underneath?
Your approach looks good to me. You just need to override the draw_item method in your Picture and Video models. Your template will look something like
{% for post in posts %}
{{ post.media_content.draw_item }}
{% endfor %}
and it doesn't matter which model the generic foreign key points to, as long as it has a draw_item method defined.

Categories