Django loop is returning multiple posts - python

I am trying to get django to display posts from a single catgory on that category's page, instead, it displays all the categories and the posts under each category in the template.
Here are my models (I trimmed them down):
class Category(models.Model):
...
title = models.CharField(max_length=255, verbose_name="Title", null=True, blank=True)
slug = models.SlugField(max_length=200, unique=True)
class Petition(models.Model):
...
category = models.ManyToManyField(Category, verbose_name="Category", related_name='petitions')
...
views.py:
class CategoryMixin(object):
def get_context_data(self, **kwargs):
context = super(CategoryMixin, self).get_context_data(**kwargs)
context['categories'] = Category.objects.all()
return context
class CategoryView(CategoryMixin, generic.ListView):
model = Category
categories = Category.objects.all() # this will get all categories, you can do some filtering if you need (e.g. excluding categories without posts in it)
queryset = Category.objects.all()
template_name = 'petition/category_list.html'
class CategoryIndexView(generic.ListView):
model = Category
template_name = 'petition/category.html'
context_object_name = 'category_list'
def get_queryset(self):
return Category.objects.prefetch_related('petitions').order_by('-created_on')[:10]
class PetitionIndexView(generic.ListView):
template_name = 'petition/home.html'
context_object_name = 'petition_list'
queryset = Petition.objects.order_by('-created_on')
def get_queryset(self):
queryset_list = Petition.objects.order_by('-created_on')
#SEARCH QUERY LOGIC
query = self.request.GET.get("q")
if query:
queryset_list = queryset_list.filter(
Q(title__icontains = query) |
Q(petition__icontains = query) |
Q(created_by__first_name__icontains = query) | #icontains = only text fields. Use text field within foreign key field
Q(created_by__last_name__icontains = query)
).distinct()
paginator = Paginator(queryset_list, 10) # Show 10 posts per page
page = self.request.GET.get('page')
try:
queryset = paginator.page(page)
except PageNotAnInteger:
# If page is not an integer, deliver first page.
queryset = paginator.page(1)
except EmptyPage:
# If page is out of range (e.g. 9999), deliver last page of results.
queryset = paginator.page(paginator.num_pages)
return queryset
context = {
'object_list' : queryset,
'title' : 'List'
}
urls.py:
url(r'^$',views.PetitionIndexView.as_view(), name="homepage"),
url(r'^petition/(?P<slug>[\w-]+)/$',views.DetailView.as_view(), name="detail"),
url(r'^categories/$', views.CategoryView.as_view(), name='category_list'),
url(r'^category/(?P<slug>[\w-]+)/$',views.CategoryIndexView.as_view(), name="category_detail"),
And my template (category.html for the CategoryIndexView) is in this format,
category.html:
{% for category in category_list %}
{% for petition in category.petitions.all %}
...
{{ petition.title }}
...
{% end for %}
{% end for %}
This shows posts in Category A, B and C in the same template.
With the views I set, how can I get it to show only posts for category A when category A is clicked?
Also, the ordering is not working properly. It is not adhering to the ordering I gave it in the view

You're using the wrong view class. A ListView is to show a list of instances of the class; so a CategoryIndexView would be to show the list of categories. But you don't want a list of categories; you want a single category, along with a list of its petitions. So, you should use a DetailView.
class CategoryDetailView(generic.DetailView):
model = Category
template_name = 'petition/category.html'
context_object_name = 'category'
def get_queryset(self):
return Category.objects.prefetch_related('petitions')
And the template is just:
{% for petition in category.petitions.all %}
...
{{ petition.title }}
...
{% end for %}
Edit: I changed the context_object_name from 'category_list' to 'category' and the template is returning the right posts now.

I'm guessing that every time you change categories, the whole loop executes again, without changing the contents of category_list (which initially is ["A", "B", "C"]). In that case, even though you click on the button to change category, the loops displays the contents of the categories "A", "B" and "C" again regardless.
Have you tried adding an if statement that checks in which category you are at the moment and only show the posts in that category?
{% for category in category_list %}
{% if category.name == ACTUAL_CATEGORY_TO_DISPLAY %}
{% for petition in category.petitions.all %}
...
{{ petition.title }}
...
{% end for %}
{% end if %}
{% end for %}
Edit:
As #Daniel Roseman pointed out in a comment, this is a quite inefficient solution. I've just posted it because I'm guessing if this is really what is happening to you.
If this is the case you shouldn't be doing this. One of the proper solutions is making a new request that retrieves all the posts for that given category every time you change it.
Anyhow, I'm just guessing here. For us to give you a better insight you should post the template code that does the category change.

Related

How to render information from a ForeignKey field through DetailView class?

I have two models (Taxonomia and Distribucion) which are the following:
# models.py file
class Taxonomia(models.Model):
id_cactacea = models.AutoField(primary_key=True)
subfamilia = models.CharField(max_length=200)
class Distribucion(models.Model):
id_distribucion = models.AutoField(primary_key=True)
localidad = models.TextField(null=True, blank=True)
taxonomia = models.ForeignKey(Taxonomia, on_delete=models.CASCADE, default=1)
As you can see in Distribucion there is a one to many relationship with the Taxomia table.
Implement the two models in the "admin.py" file so that you can edit the Distribucion table from Taxonomia
class DistribucionInline(admin.TabularInline):
model = Distribucion
extra = 0
class TaxonomiaAdmin(admin.ModelAdmin):
actions = None # desactivando accion de 'eliminar'
list_per_page = 20
search_fields = ('genero',)
radio_fields = {"estado_conservacion": admin.HORIZONTAL}
inlines = [DistribucionInline]
admin.site.register(Taxonomia, TaxonomiaAdmin)
In turn, the file "view.py" renders the Taxonomia table as follows:
from repositorio.models import Taxonomia, Distribucion
class CactaceaDetail(DetailView):
model = Taxonomia
template_name = 'repositorio/cactacea_detail.html'
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['distribuciones'] = Distribucion.objects.all()
return context
I tried to access to context ['distribuciones'] information from the template as follows without getting any results:
{% for obj in object.distribuciones %}
{{ obj.localidad }}
{% endfor %}
OBS: For each Taxonomia element there will be four elements from the Distribucion table, so I need to use a FOR loop
Is the way I add the information from the Taxonomia table in the "CactaceaDetail" view correct?
Is the way I read the information in the template correct?
How could I visualize all the information that "CactaceaDetail" sends to the template using the DJANGO shell so that I can debug better in the future?
Thank you.
Try removing the "object" from your for loop in the template:
{% for obj in distribuciones %}
{{ obj.localidad }}
{% endfor %}
The reason is because you are passing distribuciones in the regular context, not as part of the class object so you can't reference it with object.distribuciones.

Use Key-Value Lookup for Second Model in Django Template For Loop?

I'd like to figure out a way to use a key-value lookup for a second model within a Django for loop.
This question on dictionary key-value loops in templates is on the right track, but I am using two normalized data models. There is a 'Parent' data object (EventDetail) that contains the relevant link and 'Child' data object (DateEvent) that has date-specific information with data on user actions.
I have a template tag, link_if_exists, that is being duplicated many times. Django-debug-toolbar tells me this is being duplicated 76 times right now. This 'duplicate' message is, itself, duplicated many times.
This is what I have now:
app_extras.py
#register.filter()
def link_if_exists(title):
"""Return a link if it exists"""
event_link = None
try:
event_link = EventDetail.objects.filter(title=title).values_list('link', flat=True)[0]
except IndexError:
pass
if event_link != "":
return event_link
return False
template.html
{% load 'app_extras' %}
{% for item in child_date_items %}
{% if item.title|link_if_exists %}
{{item.title}}
{% endif %}
{% endfor %}
models.py
class DateEvent(models.Model)
title = models.CharField(max_length=250)
event_date = models.DateField(default=date.today)
event_datetime = models.DateTimeField(auto_now=False)
class EventDetail(models.Model)
title = models.CharField(max_length=250)
link = models.URLField(max_length=200, default="", blank=True)
views.py
class ProblemView(TemplateView):
template_name = "template.html"
def get_context_data(self, **kwargs):
context = super(ProblemView, self).get_context_data(**kwargs)
date_today = utils.get_date_today()
child_date_items = DateEvent.objects.filter(event_date=date_today)
context['child_date_items'] = child_date_items
return context
Django-debug-toolbar output
SELECT link FROM
table WHERE title='title'
...
| duplicated 74 times
Something like this is what I am after:
Add 'event_detail' to views.py
class NewView(TemplateView):
template_name = "template.html"
def get_context_data(self, **kwargs):
context = super(NewView, self).get_context_data(**kwargs)
date_today = utils.get_date_today()
child_date_items = DateEvent.objects.filter(event_date=date_today)
context['child_date_items'] = child_date_items
event_detail = EventDetail.objects.all()
context['event_detail'] = event_detail
return context
Lookup title as key to get 'link' value in ideal_template.html
{% for item in child_date_items %}
{% if event_detail[item.title]['link'] %}
{{item.title}}
{% endfor %}
{% endfor %}
I don't know if this functionality exists so I am open to other suggestions. I am also open to computing this in views.py and iterating over a common object in the template. I understand that I could duplicate the link data and just add a link column in the DateEvent model, but that seems wasteful and I'd like to avoid that if possible. This isn't the only field I need this type of logic, so adding everything to the Child object would take up a lot of extra space in the database.
You need to do some work on your models. If there is a one-to-many relationship between them (several DateEvents for one EventDetail), then rather than duplicating title manually in both of them and then linking both again manually, you should formalize the relationship at the model level:
class EventDetail(models.Model)
title = models.CharField(max_length=250)
link = models.URLField(max_length=200, default="", blank=True)
class DateEvent(models.Model)
event_detail = models.ForeignKey(EventDetail, on_delete=models.CASCADE)
event_date = models.DateField(default=date.today)
event_datetime = models.DateTimeField(auto_now=False)
Then you can refer to the link field from any DateEvent object without any conditionals and duplicate queries (if you use select_related() properly).
So if you're working with DateEvents, you'd use:
date_events = (
DateEvent.objects
.filter(event_date=date_today)
.select_related('event_detail')
)
Then, in the template:
{% for item in child_date_items %}
{{item.event_detail.title}}
{% endfor %}

How to filter query in Django template? Need to filter posts based on category

Here is view:
def all(request):
products = Product.objects.all()
context = {'products': products}
template = 'products/all.html'
return render(request, template, context)
And model:
class Product(models.Model):
title = models.CharField(max_length=120)
description = models.TextField(null=True, blank=True)
category = models.ManyToManyField(Category, blank=True)
Now, in template, I want to get posts in various areas based on category. I tried with:
{% for product in products %}
{% if product.category == "womens-clothin" %}
but it didn't work. Also, tried on views:
products = Product.objects.filter(category='womens-clothin')
but none worked. How to figure this out?
You cant do like this,
products = Product.objects.filter(category='womens-clothin')
because category is type of ManyToManyField not string.
First take Category object. Something like ,
some_category = Category.objects.get(category_name="SOMETHING")
and then perform,
products = Product.objects.all().filter(category=some_category)

Where/how is Django ManyToManyField represented in the database?

UPDATE: Just found out that the ManyToManyField is causing the admin interface to crash, when a specific album is selected. I commented them out, commented out all references to it, reran makemigrations and migrate, and now the admin interface works again...which leaves me even farther away from making this "favorite" column work :( See this followup: Why is Django ManyToManyField causing admin interface to crash? Why is no through table being created?
Background: My goal is to make the "Favorite?" column in this webpage reflect the favorite albums of the currently-logged-in user, where each is either "no" or "yes", and is a clickable link to toggle the choice. (When not logged in, they will all be grey "n/a"-s.)
Therefore, for each album, there may be exactly zero or one "has favorited" entry per user. If the entry exists, they've favorited it. If it doesn't exist, they didn't.
Here is my Album model, with the favorited_by_users many-to-many column (full models.py at the bottom):
class Album(models.Model):
OFFICIALITY = (
('J', 'Major studio release'),
('I', 'Non-major official release'),
('U', 'Unofficial'),
)
title = models.CharField(max_length=70)
description = models.TextField(max_length=500, default="", null=True, blank=True)
pub_date = models.DateField('release date')
officiality = models.CharField(max_length=1, choices=OFFICIALITY)
is_concert = models.BooleanField(default=False)
main_info_url = models.URLField(blank=False)
thumbnail = models.FileField(upload_to=get_upload_file_name, blank=True, null=True)
#virtual field to skip over the through table.
songs = models.ManyToManyField("Song", through="AlbumSong")
favorited_by_users = models.ManyToManyField(User)
def __str__(self):
return self.title
class Meta:
#Default ordering is by release date, ascending.
ordering = ['pub_date']
I originally had this FavoriteAlbum model, but since it has no extra information beyond the foreign keys, it was recommended that I eliminate it in favor of the above many-to-many column.
class FavoriteSongs(models.Model):
user = models.ForeignKey(User)
song = models.ForeignKey(Song)
class Meta:
unique_together = ('user', 'song',)
def __str__(self):
return "user=" + str(self.user) + ", song=" + str(self.song)
What I need to do is a "left join" between album and user, where all albums are selected, and any favorites by the currently-logged-in user are joined to it (None if they haven't favorited it). I don't know what to do.
I've also been told about the extra() function to do this join. The currently-working query in the view's get_queryset() is
return super(AlbumList, self).get_queryset().order_by("pub_date")
(Full views.py below.) My current guess is this:
return super(AlbumList, self).get_queryset().order_by("pub_date").extra(select={"is_favorite": "favorited_by_users__id = " + str(request.user.id) })
But, while this doesn't crash, the value of each {{ is_favorite }} in the template is nothing (the empty string). This makes sense since there's nothing in the database yet, but what now? I have no idea if this is the correct Django query.
I want to add an item in the database to test this, with a manual SQL statement in postgres (not via a Django command yet), but how and where do I do this?
I've successfully run makemigrations and then migrate with this new m2m column (and without the FavoriteSongs model), but I see nothing in the database that represents the is-favorite value. There's no extra column in billyjoel_album, and no through table akin to billyjoel_favoritealbum. So where/how is this data stored in the database?
(Any other advice regarding this extra "favorite" column would be appreciated as well!)
Thanks.
models.py
from django.db import models
from django.contrib.auth.models import User
from time import time
def get_upload_file_name(instance, filename):
return "uploaded_files/%s_%s" % (str(time()).replace(".", "_"), filename)
class Album(models.Model):
OFFICIALITY = (
('J', 'Major studio release'),
('I', 'Non-major official release'),
('U', 'Unofficial'),
)
title = models.CharField(max_length=70)
description = models.TextField(max_length=500, default="", null=True, blank=True)
pub_date = models.DateField('release date')
officiality = models.CharField(max_length=1, choices=OFFICIALITY)
is_concert = models.BooleanField(default=False)
main_info_url = models.URLField(blank=False)
thumbnail = models.FileField(upload_to=get_upload_file_name, blank=True, null=True)
#virtual field to skip over the through table.
songs = models.ManyToManyField("Song", through="AlbumSong")
favorited_by_users = models.ManyToManyField(User)
def __str__(self):
return self.title
class Meta:
#Default ordering is by release date, ascending.
ordering = ['pub_date']
class Song(models.Model):
name = models.CharField(max_length=100)
description = models.TextField(max_length=500, default="", null=True, blank=True)
length_seconds = models.IntegerField()
lyrics_url = models.URLField(default="", blank=True, null=True)
albums = models.ManyToManyField("Album", through="AlbumSong")
favorited_by_users = models.ManyToManyField(User)
def get_length_desc_from_seconds(self):
if(self.length_seconds == -1):
return "-1"
m, s = divmod(self.length_seconds, 60)
h, m = divmod(m, 60)
if(h):
return "%d:%02d:%02d" % (h, m, s)
else:
return "%d:%02d" % (m, s)
def __str__(self):
return self.name
class AlbumSong(models.Model):
song = models.ForeignKey(Song)
album = models.ForeignKey(Album)
sequence_num = models.IntegerField()
class Meta:
unique_together = ('album', 'sequence_num',)
unique_together = ('album', 'song',)
def __str__(self):
return str(self.album) + ": " + str(self.sequence_num) + ": " + str(self.song)
views.py
from .models import Album, Song, AlbumSong
from datetime import datetime, timedelta
from django.core.context_processors import csrf
from django.shortcuts import render, render_to_response
from django.views.generic import DetailView, ListView
from enum import Enum
def get_str_with_appended(string, between_if_str_non_empty, new_value):
if(len(string) == 0):
return new_value
else:
return string + between_if_str_non_empty + new_value
class PrependQuestionMark(Enum):
YES, NO = range(2)
def get_url_param_string_from_params(prepend_question_mark=PrependQuestionMark.YES, **kwargs_all_params):
param_list = ""
for key in iter(kwargs_all_params):
value = kwargs_all_params[key]
if(value is not None):
param_list = get_str_with_appended(param_list, '&', str(key) + "=" + str(value))
if(len(param_list) == 0):
return param_list;
if(prepend_question_mark == PrependQuestionMark.YES):
return "?" + param_list
else:
return param_list
class AlbumList(ListView):
model = Album
context_object_name = "albums"
#Derived from irc/#dango/tbaxter...START
def dispatch(self, request, *args, **kwargs):
#default to asc
self.sort_order = request.GET.get("sort_order", None)
self.sort_item = request.GET.get("sort_item", None)
self.csrf_token = csrf(request)["csrf_token"]
self.logged_in_user = request.user
#self.csrf_token = request.GET.get("csrf_token", None)
return super(AlbumList, self).dispatch(request, *args, **kwargs)
def get_queryset(self):
#Item zero in both is the default
#should be static global
asc_desc_list = ["asc", "dsc"]
sort_by_types = ["pub_date", "title"]
if(self.sort_order is None and self.sort_item is None):
#Use default ordering
return super(AlbumList, self).get_queryset()
#Custom ordering requested
sort_order = self.sort_order
sort_item = self.sort_item
if(sort_order is None or
sort_order not in asc_desc_list):
sort_order = asc_desc_list[0]
if(sort_item is None or
sort_item not in sort_by_types):
sort_item = sort_by_types[0]
order_minus = "" if sort_order == "asc" else "-"
return super(AlbumList, self).get_queryset().order_by(order_minus + sort_item).extra(select={"is_favorite": "favorited_by_users__id = " + str(self.logged_in_user.id) })
def get_context_data(self, **kwargs):
context = super(AlbumList, self).get_context_data(**kwargs)
context["sort_order"] = self.sort_order
context["sort_item"] = self.sort_item
context["url_params"] = get_url_param_string_from_params(
sort_item=self.sort_item,
sort_order=self.sort_order,
csrf_token=self.csrf_token)
return context
class AlbumDetail(DetailView):
model = Album
context_object_name = "album"
def dispatch(self, request, *args, **kwargs):
#default to asc
self.sort_order = request.GET.get("sort_order", None)
self.sort_item = request.GET.get("sort_item", None)
self.csrf_token = csrf(request)["csrf_token"]
return super(AlbumDetail, self).dispatch(request, *args, **kwargs)
def get_context_data(self, **kwargs):
#Call the base implementation first to get a context
context = super(AlbumDetail, self).get_context_data(**kwargs)
#Add in the required extra info: album_songs, ordered by
#sequence_num
#select_related is to query the database for all songs at once, here
#in the view, to prevent the template from pin-pricking the database
#in each for loop iteration. For large datasets, this is critical.
context['album_songs'] = kwargs["object"].albumsong_set.order_by('sequence_num').select_related("song")
context["url_params"] = get_url_param_string_from_params(
sort_item=self.sort_item,
sort_order=self.sort_order,
csrf_token=self.csrf_token)
return context
album_list.html
{% extends "base.html" %}
{% load bj_filters %}
{% block title %}Billy Joel Album Browser{% endblock %}
{% block sidebar %}
<UL>
<LI>All albums</LI>
<LI>Admin</LI>
</UL>
{% endblock %}
{% block content %}
<TABLE ALIGN="center" WIDTH="100%" BORDER="1" CELLSPACING="0" CELLPADDING="4" BGCOLOR="#EEEEEE"><TR ALIGN="center" VALIGN="middle">
{% if user.is_authenticated %}
<TD>My profile (Logout)</TD>
{% else %}
<TD>Login to view your favorites</TD>
{% endif %}
</TR></TABLE>
<H1>Billy Joel Album Browser</H1>
<!--
<P>url_params={{ url_params }}</P>
-->
{% if albums.count > 0 %}
<P>Officiality: <IMG SRC="/static/images/major.jpg" height="20"/>=Major studio release, <IMG SRC="/static/images/minor.jpg" height="20"/>=Official release, <IMG SRC="/static/images/unofficial.jpg" height="20"/>=Unofficial</P>
<TABLE ALIGN="center" WIDTH="100%" BORDER="1" CELLSPACING="0" CELLPADDING="4" BGCOLOR="#EEEEEE"><TR ALIGN="center" VALIGN="middle">
<TD><B><U><a href="{% url 'album_list' %}?sort_item=title&sort_order=
{% if sort_item == 'pub_date' %}asc{% else %}
{{ sort_order|multival_to_str:'asc,dsc->dsc,asc,dsc' }}
{% endif %}
&csrf_token={{ csrf_token }}">Title</A></U></B><BR><I><FONT SIZE="-1">(click a title to view its song list)</FONT></I></TD>
<TD><B><U><a href="{% url 'album_list' %}?sort_item=pub_date&sort_order=
{% if sort_item == 'title' %}asc{% else %}
{{ sort_order|multival_to_str:'asc,dsc->dsc,asc,dsc' }}
{% endif %}
&csrf_token={{ csrf_token }}">Released</A></U></B></TD>
<TD>Officiality</TD>
<TD>Concert</TD>
<TD>Wiki</TD>
<TD>Favorite?</TD>
{% for album in albums %} <!-- No colon after "albums" -->
</TR><TR>
<TD VALIGN="top">
{% if album.thumbnail %}
<img src="/static/{{ album.thumbnail }}" width="25"/>
{% else %}
<img src="/static/images/white_block.jpg" width="25"/>
{% endif %}
{{ album.title }}
{% if album.description %}
<BR/><FONT SIZE="-1"><I>{{ album.description|truncatewords:10 }}</I></FONT>
{% endif %}
<TD>{{ album.pub_date|date:"m/y" }}</TD>
<TD><IMG SRC="/static/images/{{ album.officiality|multival_to_str:"J,I,U->major,minor,unofficial,broken_image"}}.jpg" height="20"/></TD>
<TD>{{ album.is_concert|yesno:"Yes,No" }}</TD>
<TD>Wiki</TD>
<TD><I>n/a {{ is_favorite }}</I></TD>
{% endfor %}
</TR></TABLE>
{% else %}
<P><I>There are no albums in the database.</I></P>
{% endif %}
{% endblock %}
M2M relationships are brand-new tables created. They get an unique name, and have two foreign keys. A composite key is created so the direct and the related model combinations are unique.
When you do:
class Topping(models.Model):
name = ...
class Pizza(models.Model):
name = ...
toppings = models.ManyToManyField(Topping, related_name="pizzas")
#not including a related_name will generate a "pizza_set" related name.
A new table appears describing the relationship, with an internal name. This table has a pizza_id and a topping_id foreign key, and a composite unique key including both fields. You cannot, and should not, predict the name of such table.
On the other side, if you want to access the relationship and, possible, declare more fields, you can:
class Topping(models.Model):
name = ...
class Pizza(models.Model):
name = ...
toppings = models.ManyToManyField(Topping, related_name="pizzas", through="PizzaAndTopping")
#not including a related_name will generate a "pizza_set" related name.
class PizzaAndTopping(models.Model):
more_data = models.TextField()
pizza = models.ForeignKey(Pizza, null=False)
topping = models.ForeignKey(Topping, null=False)
class Meta:
unique_together = (('pizza','topping'),)
Notice how I added a through parameter. Now you have control of the middle-table BUT you cannot add or delete models from the relationship. This means, with this approach you cannot:
Pizza.objects.get(pk=1).toppings.append(Topping.objects.get(pk=2))
Nor you can remove, nor you can do these operations in the Topping side of the life.
If you want to add or delete toppin-pizza relationships, you must do it directly in the PizzaAndTopping relationship.
If you want to know whether the current user has marked any song as their favorite, you should prefetch the relationship. In Django 1.7, you can prefetch a M2M related field using a custom filter: you can fetch all the albums, and only a query getting the current user, using a Prefetch object. See the official docs about prefetching here.
Antoher solution would involve:
fetching the current user's favorite albums list: ulist = user.album_set.all()
fetching the current page of alboms: _list = Album.objects.all()[0:20]
fetch values of user albums: ulist = ulist.values_list('id', flat=True)
[1, 2, 4, 5, 10, ...] #you'll get a list of ids
when iterating over each album in the page, you test currentAlbum.id in ulist and print a different message (either yes or no).
The many-to-many field is represented in the database in exactly the same way as your original FavouriteSongs model - as a linking table with ForeignKeys to both Song and User. The only benefit of getting rid of FavouriteSongs is that you're now using an automatically-defined through table, rather than a manual one.
I don't understand your example query, since you don't say what model you are actually calling it on, or what self.logged_in_user is. However, you can't use extra like this: you are trying to put Django query syntax there, complete with double-underscore names to traverse relationships, but extra is passed directly to the SQL, and that doesn't know anything about that syntax.
I would not attempt to do this in one query. Instead I would do two queries, one to get all the albums and one to get the user's favourites. get_queryset would just return the full album list, and then you can use get_context_data to get an additional set of objects representing the IDs of the favourites:
favorites = self.logged_in_user.album_set.all().values_list('id', flat=True)
context['favorites'] = set(favorites)
The values_list just gets the IDs of the albums only, since that's all we need, and we then put them into a set to make lookups quicker.
Now, in the template, you can just do:
{% for album in albums %}
...
<td>{% if album.id in favorites %}Yes{% else %}No{% endif %}</td>
{% endfor %}

Getting ObjectID of mongodb elements from HTML template in django

I am using Python + MongoDB (MongoEngine) for a simple reservation based application.
The application has two models : Restaurants and Reservations.
class Restaurant(Document):
name = StringField(max_length=200, required=True)
location = StringField(max_length=200, required=True)
class Reservation(Document):
restaurant_id = ReferenceField(Restaurant, reverse_delete_rule=CASCADE)
time = IntField(min_value=1, required = True)
number_of_people = IntField(min_value=1, required = True)
Reservations has a ReferenceField to Restaurants. [MongoEngine using DBRef internally]
Now, when I render the list of restaurants, I have to give an option of reservation corresponding to each restaurant. In the Django template served corresponding to List of Restaurants, I have included all the three field of restaurant element: restaurant.id restaurant.name and restaurant.location.
Following is the template corresponding to restaurant list.
{% extends "base.html" %}
{% block content %}
{% for restaurant in restaurant_list %}
<div style="margin-bottom: 20px;">
<h4>{{ restaurant.name }}</h4>
<h4>{{ restaurant.location }}</h4>
Reserve Table
</div>
{% endfor %}
`{% endblock %}
Now if a user clicks on reserve, I need to create a form (using CreateView and forms.Forms of Django).
But the problem is that to save the form data (i.e. an entry in the Reservation collection of MongoDB), I need the reference object of the restaurant. How can I get that ?
I have not used MongoDB for some time, but I believe your question has nothing to do with MongoDB, so I will answer on how you can get a reference to one record (restaurant) when creating another (reservation).
You are not showing what {{ restaurant.get_reserve_url }} is, but in general, you have three options:
1.- You add a field to the form to have the user enter the restaurant (via dropdown or auto-complete), but I usually a bad UX.
2.- You pass the reference to the restaurant as a URL argument:
/reservation/add/?restaurant=abc
3.- You pass the reference to the restaurant as part of the URL:
/restaurant/abc/reservation/add/
With either 2 or 3, your CreateView will have to extract the restaurant ID from the URL and get the reference to set it as a hidden value on the form.
Here is an example for how to get it from the argument (2) but it is almost the same for (3) only you get the restaurant id from the kwargs instead of request.GET:
class ResevationCreateView(CreateView):
model = Reservation
form_class = ReservationForm
template_name = 'form.html'
def form_valid(self, form):
self.object = form.save(commit=False)
restaurant_id = form.cleaned_data.get('restaurant_id')
# ... Use restaurant to create new reservation
return HttpResponseRedirect(self.object.get_absolute_url())
def get_form_kwargs( self ):
kwargs = super(ResrvationCreateView, self).get_form_kwargs()
if 'restaurant' in self.request.GET:
restaurant_id = self.request.GET['restaurant']
kwargs['restaurant'] = however_you_get_a_restaurant_obj(restaurant_id)
else:
kwargs['restaurant'] = None
return kwargs
and your form will be something like
class ReservationForm(forms.Form):
restaurant_id = forms.IntegerField(required=False, widget=forms.HiddenInput())
time = ...
number_of_people = ...
class Meta:
model = Reservation
fields = [...]
def __init__(self, restaurant, *args, **kwargs):
super(ReservationForm, self).__init__(*args, **kwargs)
if restaurant:
self.initial['restaurant_id'] = restaurant.id
Hope this is what you needed. Let me know if I missed your point completely

Categories