Django ListView dynamic context data - django

Given my models:
class Deck(models.Model):
owner = models.ForeignKey(User, on_delete=models.CASCADE)
name = models.CharField(max_length=255)
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()
In my html template, I want to have a table of all the user's decks, each with their number of cards, e.g. like this:
Deck1: 15
Deck2: 22
Deck3: 100
In my views I have:
def get_context_data(self,**kwargs):
context=super(generic.ListView,self).get_context_data(**kwargs)
context['number_decks']=Deck.objects.filter(owner=self.request.user).count
context['number_cards']=Flashcard.objects.filter(owner=self.request.user,deck__name="Deck1").count
number_decks works as expected.
number_cards also works when I manually type the name in.
But how can I do this without specifying the name, so that in the html template I can just say:
{% for deck in deck_list %}
<td>{{deck.name}}</td>
<td>{{number_cards}}</td>
{% endfor %}
And since I won't know the names of the decks that users will create.
I've tried deck__id=deck.id, but I get zeroes.
How can I change either my models or views to get what I want?

You could just write that as a function to your Deck-Model:
class Deck(models.Model):
owner = models.ForeignKey(User, on_delete=models.CASCADE)
name = models.CharField(max_length=255)
def count_flashcards(self):
fc = Flashcard.objects.filter(deck=self).count()
return fc
And in your template:
{% for deck in deck_list %}
<td>{{deck.name}}</td>
<td>{{deck.count_flashcards}}</td>
{% endfor %}
You might want to use a cached attribute decorator here as well

Related

How to access 'related_name' of a model via intermediate model's Foreign key inside template tag?

models.py
class Book(models.Model):
title = models.CharField(max_length=100)
author = models.ForeignKey(User, on_delete=models.CASCADE)
active = models.BooleanField(default=True)
...
class Review(models.Model):
paper = models.ForeignKey(Book, null=True, on_delete=models.CASCADE, related_name='book_class_related_name')
user = models.ForeignKey(User, on_delete=models.CASCADE)
comment = RichTextField()
status = models.CharField(max_length=10, choices=options, default='draft')
...
class TrackReviewRequests(models.Model):
paperid = models.ForeignKey(Book, null=True, on_delete=models.CASCADE, related_name='book_track')
numberOfTimesReviewRequestSent = models.PositiveSmallIntegerField(default=0)
...
views.py
reviews_in_draft = Review.objects.filter(paper__active=True).filter(status='draft')
return render(request,
'accounts/profile.html',
{
'reviews_in_draft': reviews_in_draft,
})
profile.html
Here I tried accessing the 'numberOfTimesReviewRequestSent' using the following code:
{% for review in reviews_in_draft %}
{{ review.paper.book_track.numberOfTimesReviewRequestSent }}
{% endfor %}
But I am getting empty string.
Then I wrote a method inside the Book model
def get_TrackReviewRequests_numberOfTimesReviewRequestSent(self):
return self.book_track.numberOfTimesReviewRequestSent
and tried accessing the numberOfTimesReviewRequestSent in the profile.html using the following code:
{{ review.paper.get_TrackReviewRequests_numberOfTimesReviewRequestSent }}
But this time I got the error stating
'RelatedManager' object has no attribute 'numberOfTimesReviewRequestSent'
Ultimately, I want to access the numberOfTimesReviewRequestSent in the template using the context variable.
A Bookcan have multiple TrackReviewRequests (Since TrackReviewRequests has a foreign key to Book, if in reality there can only be one then you should use a OneToOneField [Django docs] instead) hence review.paper.book_track is not an instance of TrackReviewRequests but as the error says a RelatedManager.
Hence when you write review.paper.book_track.numberOfTimesReviewRequestSent it doesn't make much sense. You can instead loop over the related instances if you want in the template like so:
{% for review in reviews_in_draft %}
{% for review_request in review.paper.book_track.all %}
{{ review_request.numberOfTimesReviewRequestSent }}
{% endfor %}
{% endfor %}

django relationships into a cbv

I have this two models:
class MenuGroup(models.Model):
name = models.CharField(max_length=30)
class MenuProduct(models.Model):
name = models.CharField(max_length=30)
ingredients = models.CharField(max_length=250)
price = models.FloatField(null=True, blank=True, default=0.0)
group = models.ForeignKey(MenuGroup, on_delete=models.CASCADE)
I want to get all the records of MenuGroup with all the MenuProduct records related.
Template side i need to have structure like this:
{% for group in menugroups %}
...
{% product in group.menuproducts %}
How to express this into a Django CBV view?
You don't need to do anything, Django gives you that functionality already. The only thing to change is that the default accessor is <modelname>_set, and it's a Manager; so:
{% for product in group.menuproduct_set.all %}

Problem with set value in view and template

I want to create a list where the user can give titles to his list in which he selects in which category he should be, this is how the model looks like
class UserListAnime(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
anime = models.ManyToManyField(Anime, through='ListAnime')
class Anime(models.Model):
title = models.CharField(max_length=200)
slug = extension_fields.AutoSlugField(populate_from='title', blank=True)
class ListAnime(models.Model):
LIST_CHOICES = (
(WATCHING, 'Oglądam'),
(PLANNING, 'Planuję'),
(COMPLETED, 'Ukończone'),
(DROPPED, 'Porzucone'),
(ONHOLD, 'Wstrzymane'),
(MISSING, 'Pomijam'),
)
user = models.ForeignKey(UserListAnime, on_delete=models.CASCADE, null=True, blank=True)
anime = models.ForeignKey(Anime, on_delete=models.CASCADE, null=True, blank=True)
type = models.CharField(max_length=30, choices=LIST_CHOICES, null=False, blank=False)
In the view I only have to take a list of the user and I have displayed it but I want it to be filtered through the type in ListAnime
def ListAnimeView(request, pk, username):
list_anime = UserListAnime.objects.filter(user__pk=pk, user__username=username,
listanime__type='ogladam',
anime__listanime__type='ogladam').all()
context = locals()
and html looks like
{% for list in list_anime.anime.all %}
{{ list }}
{% endfor %}
My question is how to extract all records when type = LIST_CHOICES and show this in html
EDIT: SOLVED just need change in view from UserListAnime,objects.. to ListAnime.objects
and in html should be
{% for list in list_anime %}
{{ list.anime }}
{% endfor %}
I don't know exactly what are you trying to achieve. Do you want to get a list of ListAnime entities given a user, and get only which are of type 'ogladam'?
def listAnimeView(request, pk):
list_anime = ListAnime.objects.filter(user__user_id=pk, type='ogladam')
return render(request, 'template-name', context=['list_anime': list_anime])'

How to access extended User attributes in django template

I am trying to access extended field of User model in Django template but it doesn't work, there are my files:
models.py:
class Author(models.Model):
user = models.OneToOneField(User, related_name='user', on_delete=models.SET_NULL, null=True)
bio = models.TextField(
max_length=1400, help_text="Enter author biography.")
def __str__(self):
return self.user.username
def create_profile(sender, **kwargs):
user = kwargs["instance"]
if kwargs["created"]:
user_profile = Author(user=user, bio='my bio')
user_profile.save()
post_save.connect(create_profile, sender=User)
template:
{% extends "base_generic.html" %}
{% block title %}
<title>Author {{user}}</title>
{% endblock %}
{% block content %}
<h1>{{user}}</h1>
<h2>Bio:</h2>
<p>{{user.author.bio}}</p>
<div>
{%for item in user.author.blogs_set.all%}
<p>{{item.title}}</p>
<hr>
{%endfor%}
</div>
{% endblock %}
views:
class UserDetailView(generic.DetailView):
model = User
template_name = 'blogapp/user_detail.html'
I want to get access to the bio field through user.author.bio but nothing displays I have also tried user.bio is there any tricky way to get access to this field?
You set the related_name to:
class Author(models.Model):
user = models.OneToOneField(
User,
related_name='user',
on_delete=models.SET_NULL, null=True
)
But the related_name is the name to access the related Author from a User object (so the name of the relation in reverse). You thus should set it to author (or leave it blank), like:
class Author(models.Model):
user = models.OneToOneField(
User,
related_name='author',
on_delete=models.SET_NULL, null=True
)
By setting it to user, you could have accessed the Author object with user.user, but I strongly advise not to do this, since in the end, it will only result in code that is hard to understand. For Django it of course does not matter (given no two relations originating from User have the same name), but for programmers, it gives a wrong impression.

Filter with Q inside a span relationship

I try to get tasks in context to the project they are belonging to. With an easy Project.objects.all() i get pretty much what i want. How can i filter this queryset in the best way?
Models:
class Project(models.Model):
projectname_text = models.CharField('Projectname', unique=True, max_length=200)
class Task(models.Model):
project = models.ForeignKey(Project, on_delete=models.CASCADE,
related_name='tasks')
author = models.ForeignKey(User, null=True, blank=True, related_name='author')
editor = models.ForeignKey(User, null=True, blank=True, related_name='editor')
I tried with Q, but the result looks like it contains too many items and even items where the request.user is wrong. MAybe using Q is the wrong approach here?
Query in Views:
project_list = Project.objects.filter(Q(tasks__author=request.user) |
Q(tasks__editor=request.user))
Template:
{% for project in project_list %}
html
{% for task in project.tasks.all %}
html
{% endfor %}
{% endfor %}
First off, your query criteria is wrong. author and editor are on model Task not on Project, so you should do:
projects = Project.objects.filter(Q(tasks__author=request.user) |
Q(tasks__editor=request.user))
Secondly, never use list as your variable name, as that would override the default python data structure list, so you can no longer use list() to create a list after your variable declaration.