how to get a value of a field in django - django

I'm currently working on a django app like IMDB.com that have a Media ( contains tvshows and movies ) model and an Episode model with a one-to-many relationship between them to display these episodes at the TvShow page.
I managed to be able to show the episodes of a tvshow inside the page with:
def tvshow(request, tvshow_title):
tvshow = get_object_or_404(Media, title=tvshow_title)
episodes = Episode.objects.all().filter(is_published=True, tvshow=tvshow)
context = {
'tvshow': tvshow,
'episodes': episodes
}
return render(request, 'media/tvshow.html', context)
and this worked perfectly fine but I also needed to display episodes based on season and this got me kinda confused how do I know how many seasons does a tv show have when there's no field for it in the Media model, but the Episode model had a season_number field, so I tried to get to query the last episode of a tv show based on the season_number:
latest_episode = Episode.objects.order_by('-season_number').filter(is_published=True, tvshow=tvshow)[:1]
and I managed to indeed get the episode but I don't know now how to get what is the number of the season in it.
I tried
seasons = latest_episode.season_number
and
seasons = latest_episode['season_number']
and neither of them worked. please tell me if there's a better way to do it and if this way is good let me know how to get the season_number. :)

Using [:1] returns a QuerySet, which is basically a list, and not a model instance (think of it as returning [EPISODE] instead of just EPISODE. This means that you dont have access to the episode's season_number attribute.
Try this:
latest_episode = Episode.objects.order_by('-season_number').filter(is_published=True, tvshow=tvshow).first()
and then you should be able to use latest_episode.season_number

Related

Design Question: Low load data aggregation for overview page

What is the best way to achieve low load on the database or application server for this use case:
Let's say I want to build a web application that has for each user an overview page. The overview page shows in an aggregated form for each user the user's data. For example, if it were a library application it would show how many times the user visited the library in total, how many books he read in total, how many books were delivered delayed in total, how many minutes he spend in the building. Each time the user visits the overview page the up-to-date values should be displayed. While the user interacts with the site the numbers change.
What I could do is for every overview page refresh do several counts in the database. But that would be expensive.
views.py
def overview(request, userID):
booksCount = Book.objects.count()
booksReadCount = Book.objects.filter(UserID=userID, Status='read').count()
# ... many more, same way
libraryVisitedCount = LibraryVisits.objects.filter(UserID=userID).count()
# many counts like these on different tables for the user
data = {
"booksCount" : booksCount,
"booksReadCount" : booksReadCount,
# ... many more
"libraryVisitedCount" : libraryVisitedCount
}
render(..., context=data)
I have thought I could store a JSON object with the data to be presented on the overview page in a database table and I update the JSON each time an event happend on the site which affects the count of objects.
Or I could use a materiliazed view but to refresh it I would have to recalculate all the data of all users each time, right?
Other ideas? I'm using django webframework and postgres database.
TL;DR: I wondered isn't there a better way to receive counts than do several counts in the database each time?
Thanks.
Lets say, in Book, LibraryVisit etc models, there is ForeignKey to User model with related_name like this:
class Book(models.Model):
UserID = models.ForeignKey(User, related_name='books', on_delete=DO_NOTHING)
class LibraryVisit(models.Model):
UserID = models.ForeignKey(User, related_name='library_visit', on_delete=DO_NOTHING)
Then you can use annotation and conditional expression like this:
from django.db.models import Case, IntegerField, Sum, When
def overview(request, userID):
users = User.objects.filter(pk=userId)
users = users.annotate(
booksReadCount=Sum(
Case(
When(book__Status='read', then=1),
output_field=IntegerField()
)
)
).annotate(library_visited_count=Count('library_visit'))
# FYI: please use snake_case when defining object attribute(like model fields) as per PEP-8 style guide
data = {
"user_object" : users.first(), # taking first item of the User queryset. Also DB is hit once in this step
"booksCount" : Book.objects.count()
}
# access counts in view like this:
# user.book_read_count
# user.library_visited_count
return render(..., context=data)
# bold marked words are related_name
And render counts in template like this:
{{ user_object.book_read_count }}
{{ user_object.library_visited_count }}

Click on row in django-tables2 to take me to another table?

I basically want to click on a row or linked element and take me to another filtered table.
For instance, let's say I have a table of genres:
Romance
Horror
Comedy
I click on Romance, and then I want a list of all the romance books. How might I do this with django-tables2?
How do I get what the user clicked on in my django table?
I tried to use
table.py
class GenreTable(tables.Table):
genre = tables.LinkColumn('books_genre_table')
class Meta: ...
This doesn't work because I can't pass the data and filter it to make a table in the next view/html 'books_genre_table.html'
EDIT: I already have a detail page for genres that is different than this page that I want to create. Furthermore, I want to filter the data in a new way that does not need to be saved as a detail page with a 'pk'. I just want to have all of these go to one url.
Lets say you have a genre URL like this:
path('some_path/<int:pk>/', genre_book_view, name='books_genre_table')
and have a view like this(here I will be using reverse relation to fetch books from genre):
def genre_book(request, pk):
# will be using genre here to fetch all the books
genre = Genre.objects.get(pk=pk)
context = {
'books': genre.book_set.all() # using reverse relation to get the books
}
return render(request, 'some_template.html', context)
Then you can link your view like this:
class GenreTable(tables.Table):
genre = tables.LinkColumn('books_genre_table', args=[A('pk')])
class Meta:
...

Django Admin list values from many to many fields

For a podcast site I do have a Guest model which (stripped down) looks like this.
class Guest(models.Model):
...
episodes = models.ManyToManyField(Episode)
name = models.CharField(max_length=100, blank=False)
...
also there are a few other models connected through many to many fields with the guest model like Job and Topic (simple models with one or two CharField with some information and the ManyToManyField relation with Guest.
Now I want to display The Job(s) of a guest and his Topics in the Admin List of Guests. Also for the linked Episodes Model I want to count the amount of episodes a guest was in and also show the date (published_at field in Episode model) of the newest and the oldest Episode the guest was in.
I tried a few things already but I can't seem to get the data from the many to many fields into that list display. I'm on Django 2.0.7
You cant just show, you make create one function and JOIN the values together...
class AdminGuest(admin.ModelAdmin):
list_display = ('_episodes')
def _episodes(self, obj):
return "\n".join([a.nome for a in obj.episodes.filter()])
_episodes.short_description = "List of Episodes"

Django RadioButtons with icons and not label?

i have an app that have 2 fields: company_name and logo, i'm displaying the companies like radiobutton in my Form from Model, but i want to show the logo company instead of the company label (company name)
Any idea ?
My forms:
class RecargaForm(ModelForm):
compania = forms.ModelChoiceField(queryset=Compania.objects.all(), initial=0 ,empty_label='None', widget=forms.RadioSelect())
class Meta:
model = Recarga
Thanks :)
You could try something similar to the technique I propose in the answer to this (my own) question: How do I create a Django form that displays a checkbox label to the right of the checkbox?
Instead of writing a filter that flips the markup around, you'll need to somehow get the filter to replace the labels with the appropriate images.
Admittedly, this sounds kinda awkward to work with.

Beginner Django Forms Question: Adding an Item to a User's Categories

I have in my models Item with a many-to-many connection with Categories, and Categories have a Foreign Key to User.
What I'm hitting a road block figuring out is how to create a view with the intent to import an Item object to one or more of a User's Categories.
In it's most basic implementation I would like the view to display only the list of Categories that the User owns, and have the view process the form so that the Item is added to the appropriate Categories.
I've been struggling trying to figure out how to start this, including how to pass a User's categories to the form.
Thanks.
in the form class (ItemForm) do this
def __init__(self,user,*args,**kwargs):
super(ItemForm,self).__init__(*args,**kwargs)
self.fields['categories'] = forms.ModelMultipleChoiceField(
queryset=Categories.objects.filter(user=user))
then in your view call the form with :
form = ItemForm(request.user)
or
form = ItemForm(request.user, request.POST)
this should get you started. hopefully you can work out what you need to do from there. your question didn't leave much else to go by.