Can't use Django's get_absolute_url in dictionary of dictionaries? - django

I'm having some trouble using get_absolute_url in a template. It seems to work fine if I just pass in one of my store objects and say {{ store.get_absolute_url }}, but if I have to iterate through a dictionary of stores and then use the get_absolute_url function, it returns nothing. Exactly what I'm doing is below:
class Store(EthicalObject):
type = "Store"
name = models.CharField(max_length=50)
company = models.ForeignKey(Company, verbose_name="Company", null=True, blank=True)
location = models.OneToOneField(Location, verbose_name="Location", null=True, blank=True)
products = models.ManyToManyField('Product', related_name="%(class)s_related", db_table=u'ethicsdb_products_to_stores', blank=True)
companies = models.ManyToManyField('Company', related_name="%(class)s_related", db_table=u'ethicsdb_companies_to_stores', blank=True)
def get_absolute_url(self):
return ('store_details', [str(self.id)])
get_absolute_url = models.permalink(get_absolute_url)
This works:
views.py:
def fetch_sidebar_data(shop_object):
sidebar_modules = {}
if shop_object.content_type.name == 'company':
sidebar_modules['related_stores'] = shop_object.stores.all()
sidebar_modules['related_products'] = shop_object.products.all()
if shop_object.content_type.name == 'store':
sidebar_modules['related_companies'] = shop_object.companies.all()
sidebar_modules['related_products'] = shop_object.products.all()
if shop_object.content_type.name == 'product':
sidebar_modules['related_stores'] = shop_object.stores.all()
sidebar_modules['related_companies'] = shop_object.companies.all()
sidebar_modules['tags'] = shop_object.tags
return sidebar_modules['related_stores'][1]
def company_details(request, company_id):
company = get_object_or_404(Company, id=company_id)
sidebar_modules = fetch_sidebar_data(company)
return render_to_response('company/details.html', {'company': company, 'sidebar_modules': sidebar_modules}, context_instance=RequestContext(request))
template:
{% extends "base-onecol.html" %}
{% block page_div_extra_attr %}class="twocol"{% endblock %}
{% block sidebar_content %}
<div id="sidebar-right">
<h1>{{ sidebar_modules.name }}{{sidebar_modules.get_absolute_url }}</h1>
</div>
{% endblock %}
This doesn't work:
views.py:
def fetch_sidebar_data(shop_object):
sidebar_modules = {}
if shop_object.content_type.name == 'company':
sidebar_modules['related_stores'] = shop_object.stores.all()
sidebar_modules['related_products'] = shop_object.products.all()
if shop_object.content_type.name == 'store':
sidebar_modules['related_companies'] = shop_object.companies.all()
sidebar_modules['related_products'] = shop_object.products.all()
if shop_object.content_type.name == 'product':
sidebar_modules['related_stores'] = shop_object.stores.all()
sidebar_modules['related_companies'] = shop_object.companies.all()
sidebar_modules['tags'] = shop_object.tags
return sidebar_modules
template:
{% extends "base-onecol.html" %}
{% block page_div_extra_attr %}class="twocol"{% endblock %}
{% block sidebar_content %}
<div id="sidebar-right">
{% for module_name,module in sidebar_modules.items %}
{% ifequal module_name "related_stores" %}
<h3>Sold Here</h3>
{% for related_store in module.values %}
{{ related_store.name }}<br/>
{% endfor %}
{% endifequal %}
{% ifequal module_name "related_products" %}
<h3>Buy Local</h3>
{{ module }}<br/>
{% endifequal %}
{% ifequal module_name "related_companies" %}
<h3>
{{ module }}<br/>
{% endifequal %}
{% ifequal module_name "tags" %}
{{ module }}<br/>
{% endifequal %}
{% endfor %}
</div>
{% endblock %}
In the second one, I just get no return from get_absolute_url. I know it's working in other places when I print it out. Is this a Django bug, the inability to use get_absolute_url in a dictionary of dictionaries?

Wow, that was a rather convoluted question.
Your problem is here: {% for related_store in module.values %}
module is a QuerySet. .values is calling the QuerySet method which returns a dictionary containing the field values for each row. A dictionary has no get_absolute_url attribute, and get_absolute_url isn't a field in the model.
Just use {% for related_store in module %} and you'll be dealing with actual model instances rather than dictionaries, which means {{ related_store.get_absolute_url }} will work fine.

Related

Retrieve the human readable value of a charfield with choices via get_F00_display in Django views.py

What I want to do :
Display the human readable value of a charfield with choices via get_F00_display or other in views.py and then in template.
I have a Leave model for leaves management and want to display a template with all the leaves associated with the authenticated user.
What I have done :
Of course, I've read Django documentation (4.1) and find something interesting with get_F00_display but cannot make it works fine.
model.py (simplified)
class Leave(CommonFields):
LEAVES_TYPES = [
('10', _('Type 1')),
('20', _('Type 2')),
('30', _('Type 3')),
]
owner = models.ForeignKey(User, on_delete=models.CASCADE, null=True, blank=True)
type = models.CharField(max_length=3, choices=LEAVES_TYPES, null=True, default="10")
def __str__(self):
return self.owner.first_name + " " + self.owner.last_name + " : du " + self.begin_date.strftime("%d-%m-%Y") + " au " + self.end_date.strftime("%d-%m-%Y")
views.py
from django.shortcuts import render
from django.utils.translation import gettext as _
from .models import Leave
from django.views import generic
class LeaveListView(generic.ListView):
model = Leave
context_object_name = 'leave_list'
def get_queryset(self):
return Leave.objects.filter(is_active=True).values('type', 'begin_date','end_date','range','comment','status')
def get_context_data(self, **kwargs):
# Call the base implementation first to get the context
context = super(LeaveListView, self).get_context_data(**kwargs)
# Create any data and add it to the context
context['colHeaders'] = ['Type',
'From',
'To',
'Range',
'Comment',
'Status',]
return context
leave_list.html
{% extends "main/datatables.html" %}
<!-- TABLE TITLE -->
{% block tableTitle %}Leaves{% endblock %}
<!-- TABLE HEADER -->
{% block tableHeader %}
{% if colHeaders %}
{% for header in colHeaders %}
<th>{{header}}</th>
{% endfor %}
{% else %}
<p>No results</p>
{% endif %}
{% endblock %}
<!-- TABLE BODY -->
{% block tableBody %}
{% if leave_list %}
{% for leave in leave_list %}
<tr>
<td>{{leave.type}}</td>
<td>{{leave.begin_date}}</td>
<td>{{leave.end_date}}</td>
<td>{{leave.range}}</td>
<td>{{leave.comment}}</td>
<td>{{leave.status}}</td>
</tr>
{% endfor %}
{% else %}
<p>No results</p>
{% endif %}
{% endblock %}
<!-- TABLE FOOTER -->
{% block tableFooter %}
{% if colHeaders %}
{% for header in colHeaders %}
<th>{{header}}</th>
{% endfor %}
{% else %}
<p>No results</p>
{% endif %}
{% endblock %}
Problem :
{{leave.type}}
returns the key of the choices but I'm trying to display the human readable name in LEAVES_TYPES like "Type 1"...
What I've tried :
Using get_F00_display this way : get_type_display in my views.py :
def get_queryset(self):
return Leave.objects.filter(is_active=True).values(get_type_display, 'begin_date','end_date','range','comment','status')
Same thing in the template but no readable name displayed...

My view is taking me to the same page over and over

Hey guys I made a model that can upload some files and then i made two views upload_list.html and upload_detail.html the list pages contains the links to the actual detail page but while clicking on the links it takes me to the same page again
Here,s the models.py
class Upload(models.Model):
image = models.ImageField(upload_to = 'images',)
file = models.FileField(upload_to = 'images/%Y/%M/%d/')
name = models.CharField(max_length = 200)
def __str__(self):
return self.name
def get_absolute_url(self):
return self.pk{}
Here,s the views.py
def upload_list(request):
upload_list = Upload.objects.all()
return render(request,'app/upload_list.html',{'upload_list':upload_list})
def upload_detail(request,pk):
upload_detail = get_object_or_404(Upload,pk = pk)
return render(request,'app/upload_detail.html',{'upload_detail':upload_detail})
Hers, the urls.py
url(r'^upload/',views.upload_list,name = 'upload_list'),
url(r'^upload/(?P<pk>[-\w]+)/$',views.upload_detail,name = 'upload_detail'),
Hers, the upload_list.html
{% extends 'app/base.html' %}
{% block content %}
{% load static %}
{% for i in upload_list %}
<div class="jumbotron">
{{i.name}}
<br>
</div>
{% endfor %}
{% include 'app/index_js.html' %}
{% endblock content %}
Here,s the upload_Detail.html
{% extends 'app/base.html' %}
{% block content %}
{% load static %}
<div class="jumbotron">
<h1>{{upload_detail.name}}</h1>
<img src="{{upload_detail.name}}" alt="'Image for you,r betterment "></img>
{{upload_detail.file}}
</div>
{% include 'app/index_js.html' %}
{% endblock conten`t %}`
Have you tried put your urls.py like this ?
url(r'^upload/$',views.upload_list,name = 'upload_list'),
url(r'^upload/(?P<pk>[-\w]+)/$',views.upload_detail,name = 'upload_detail'),
or
url(r'^upload/(?P<pk>[-\w]+)/$',views.upload_detail,name = 'upload_detail'),
url(r'^upload/$',views.upload_list,name = 'upload_list'),
The $ is a regex that indicates a end of string.
I hope that helps you

Django simple tag doesn't work in if condition

I want to customize django-admin's change form for video objects by adding block with moderation tools.
When I use custom simpletags in if condition - it doesn't work.
models.py:
class Video(models.Model):
class Meta:
db_table = 'video'
DRAFT = 1
MODERATION = 2
PUBLISHED = 3
REJECTED = 4
HOSTING_UPLOADING = 5
SUSPICIOUS = 6
PUBLICATION_STATUSES = (
(DRAFT, 'draft'),
(MODERATION, 'moderation'),
(PUBLISHED, 'published'),
(HOSTING_UPLOADING, 'hosting uploading'),
(REJECTED, 'rejected'),
(SUSPICIOUS, 'suspicious')
)
video_pk = models.AutoField(primary_key=True)
name = models.CharField(max_length=150, blank=True)
hosting_id = models.CharField(max_length=20, blank=True)
publication_status = models.PositiveSmallIntegerField(choices=PUBLICATION_STATUSES, default=MODERATION)
templatetags video_publication_statuses.py:
from api.models import Video
from django import template
register = template.Library()
#register.simple_tag
def moderation(status):
return status == Video.MODERATION
#register.simple_tag
def suspicious(status):
return status == Video.SUSPICIOUS
#register.simple_tag
def published(status):
return status == Video.PUBLISHED
#register.simple_tag
def hosting_uploading(status):
return status == Video.HOSTING_UPLOADING
#register.simple_tag
def rejected(status):
return status == Video.REJECTED
change_form.html:
{% extends "admin/change_form.html" %}
{% load video_publication_statuses %}
{% suspicious original.publication_status as suspicious_status %}
{% moderation original.publication_status as moderation_status %}
{% hosting_uploading original.publication_status as hosting_uploading_status %}
{% published original.publication_status as published_status %}
{% rejected original.publication_status as rejected_status %}
{% block after_related_objects %}
{% if original.pk %}
{% for fieldset in adminform %}
{% if fieldset.name == 'Moderation' %}
{% include "admin/includes/fieldset.html" %}
{% endif %}
{% endfor %}
<div class="submit-row">
{% if rejected_status or moderation_status or suspicious_status %}
<input type="submit" value="Publish" name="publish" >
{% endif %}
{% if published_status %}
<input type="submit" value="Reject" name="reject" >
{% endif %}
</div>
{% endif %}
{% endblock %}
When I use explicit values instead of tags it works:
{% if original.publication_status == 3 %}
<input type="submit" value="Reject" name="reject" >
{% endif %}
Please help me understand what is wrong with tags?
I believe this is happening because template tags pass strings and you're checking a string against an integer e.g. return "3" == 3
Broadly speaking though, you're putting a lot of logic in a template and I typically avoid that situation. Template tags are reserved for "presentation logic" and I take that to mean "changing the way something is presented", not changing what is viewed. That logic belongs in a view or the model itself.
It should be easy enough to put this logic on your model.
class Original(...):
def rejected(self):
return self.status == Video.rejected

Template not rendering correctly while iterating through foreign keys

I am trying to iterate over my FKs in my model such that I show all the connections through various tables. My template renders but does not show any values. Any ideas?
models.py
class State(models.Model):
state = models.CharField(max_length=255)
relevantdisease = models.ForeignKey(Disease)
relevantoption = models.ManyToManyField(Option, through='StateOption')
class StateOption(models.Model):
partstate = models.ForeignKey(State)
partoption = models.ForeignKey(Option)
relevantoutcome = models.ManyToManyField(Outcome, through='StateOptionOutcome')
class StateOptionOutcome(models.Model):
stateoption = models.ForeignKey(StateOption)
relevantoutcome = models.ForeignKey(Outcome)
outcomevalue = models.CharField(max_length=20)
views.py
def stateall(request, disease_id):
disease = get_object_or_404(Disease, pk=disease_id)
states = State.objects.select_related().filter(relevantdisease=disease_id)
context = {'disease':disease,'states': states}
return render(request, "stateall.html", context)
template.html
{% for state in states %}
<li>{% for i in state.stateoption_set.all %}</li>
<li>{% for j in i.stateoptionoutcome_set.all %}</li>
{% endfor %}
{% endfor %}
{% endfor %}
I would like the template to show up as:
State1<state>
<li>partoption</li>
<li>relevantoutcome: outcomevalue</li>
State2<state>
<li>partoption</li>
<li>relevantoutcome: outcomevalue</li>
...
Your template never outputs anything.
You're probably misunderstanding the use of the {% for %} template tag.
This:
<li>{% for j in i.stateoptionoutcome_set.all %}</li>
{% endfor %}
Outputs <li> a few times.
But this:
{% for j in i.stateoptionoutcome_set.all %}
<li>{{ j.relevantoutcome }}: {{ j.outcomevalue }}</li>
{% endfor %}
Will output a line per StateOptionOutcome found in i.stateoptionoutcome_set.all.

Django getting list of records (with FK) related to object -not rendering template correctly

I am trying to follow this previous question here:
Django: getting the list of related records for a list of objects
but can't seem to get it to work.
I get a list of owners but do not get a list of pet names. The html code doesn't seem to execute the 2nd FOR loop. Any ideas?
models.py
class Teacher(models.Model):
teacher = models.CharField(max_length=300, verbose_name="teacher")
def __unicode__(self):
return self.teacher
class Owner(models.Model):
relevantteacher = models.ForeignKey(Teacher, verbose_name="teacher")
owner = models.CharField(max_length=300, verbose_name="owner")
def __unicode__(self):
return self.owner
class PetName(models.Model):
relevantowner = models.ForeignKey(Owner, verbose_name="owner")
pet_name = models.CharField(max_length=50, verbose_name="pet Name")
def __unicode__(self):
return self.pet_name
views.py
def ownersandteachers(request):
owners = Owner.objects.all()
context = {'owners': owners}
return render(request, 'ownersandpets.html', context)
template
{% for i in owners %}
{{ i.owner }} has pets:<br />
{% for v in owners.petname_set.all %} //this doesn't seem to be executing
- {{ v.pet_name }}<br />
{% endfor %}
{% endfor %}
You are doing
{% for v in owners.petname_set.all %}
where you should be doing
{% for v in i.petname_set.all %}
owners is the queryset, but you need to get the petname_set for the individual object in the queryset which is i in this case.
Also, I would recommend a condition check if i.petname_set exists. If not, do not show the has pets: text.
{% for i in owners %}
{{ i.owner }}
{% if i.petname_set.count %} has pets:<br />
{% for v in i.petname_set.all %} //this doesn't seem to be executing
- {{ v.pet_name }}<br />
{% endfor %}
{% endif %}
{% endfor %}