Why won't Django template recognize list from object field? - django

I have an object field that stores a list. The Django template is treating the list like a string, it iterates over each character, not each list value.
Tried saving the object field in various ways, "['1','2']" and "1,2". Tried the "|make_list" filter.
models.py
class SpecialField(models.Model):
name = models.CharField(max_length=200,blank=True,null=True)
description = models.CharField(max_length=200,blank=True,null=True)
value_options = models.CharField(max_length=200,blank=True,null=True)
# value_options might be "['1','2']" or "red, green, blue"
views.py
object_field_list= SpecialField.objects.all()
context = {
'object_field_list': object_field_list,
}
return render(request, 'app1/template-detail.html', context)
template
{% for object_field in object_field_list%}
{% for list_value in object_field.value_options %}
<option>{{ list_value }}</option>
{% endfor %}
{% endfor %}
I was hoping for:
<option>1</option>
<option>2</option>
But I am getting:
<option>[</option>
<option>'</option>
<option>1</option>
<option>'</option>
<option>,</option>
<option>'</option>
<option>2</option>
<option>'</option>
<option>]</option>

You are wanting to show data from a model, so let's suppose you have a model
class SpecialField(models.Model):
name=models.CharField(max_length=101)
price = models.IntegerField(default=0)
Given the way you've set in in context you can show this in the template with
{% for obj in object_field_list %}
{{ obj.name}} - {{ obj.price }}
{% endfor %}
Obviously, you need to amend for your model
Tweak on your suggestion...it needs one more level of looping. Still can't get it to work.
models.py
class SpecialField(models.Model):
name=models.CharField(max_length=101)
price = models.IntegerField(default=0)
custom_list = models.CharField(max_length=200)
template
{% for obj in object_field_list %}
{{ obj.name}} - {{ obj.price }}
{% for list_value in obj.custom_list %}
{{ list_value }}
# this is where it's breaking for me
{% endfor %}
{% endfor %}

context = {
'object_field_list': SpecialField.objects.values_list('value_options', flat=True),
}
Should get you what you actually want to loop over.
EDIT: Sorry, I missed the fact you are storing a string rather than using an ArrayField or similar. The problem from your updated answer is the data you have isn't consistent. If it were simply all comma-separated values you could do:
object_field_list = [value_list.split(',') for value_list in SpecialField.objects.values_list('value_options', flat=True)]
but you will need some way of normalizing the data you're storing in value_options. How does the data get into the database? If it's via Django, you can apply some kind of cleaning method on the form or API endpoint that accepts the data.
Tried saving the object field in various ways, "['1','2']" and "1,2". Tried the "|make_list" filter.
If you have complete control over the incoming data, you would be better off normalizing the data: rather than storing a single value_options entry on SpecialField, you would remove that field and add a second model, e.g., SpecialFieldOption like
class SpecialFieldOption(models.Model):
name = models.CharField(max_length=200, blank=False)
field = models.ForeignKey(SpecialField, related_name='options')
# now you can do
SpecialField.objects.get(pk=1).options.all()
to loop over the list of all options for a given SpecialField.

This was the handiest solution...define a new list using split. https://stackoverflow.com/a/8318915/9268133. Thanks for everyone's help!

Related

How to refer to set objects in a django view from a template?

I have data in a database in the form like this:
collection_name|manufacturer|product_type|description|image_url
----------------------------------------------------------------
Testing |FakeCo |Bed |pretty nice|/img/1.jpg
Testing |FakeCo |Desk |pretty bad |/img/2.jpg
Testing |FakeCo |Nightstand |pretty ok |/img/1.jpg
Testing |FakeCo |Draws |pretty nice|/img/3.jpg
Initially, I was using a for loop to display fields from each result, which ends up with something like this:.
For the example data set above, what I am trying to do is display only the first result from certain fields, knowing they are identical for all rows returned, and then for remaining fields only display them when they are distinct.
I tried using sets in my django view, as another answer suggested this would eliminate duplicates and solve my issue.
My django view:
def collection_detail(request, name=None):
template = loader.get_template('/webapps/my_webapp/furniture_site/main_page/templates/main_page/product-detail.html')
products = product.objects.filter(collection_name=name)
collection_name = []
manufacturer = []
description = []
image_url = []
for product in products:
collection_name.append(product.collection_name)
manufacturer.append(product.manufacturer)
description.append(product.description)
image_url.append(product.image_url)
collection_name = set(collection_name)
manufacturer = set(manufacturer)
description = set(description)
image_url = set(image_url)
context={'products': products}
return HttpResponse(template.render(context))
My issue is, that I am unable to refer to these set items in my template.
For example, in my template using:
{% for instance in products %}
{{ instance.collection_name }} Collection <br />
{% endfor %}
returns nothing, as does
{% for instance in products %}
{{ collection_name }} Collection <br />
{% endfor %}
What is the correct way to refer to items returned via the view in the template?
Ultimately, I am trying to get a result like the following (note descrption and collection name only used once, and duplicate image urls not returned).
First of all, you're not passing the right data towards your template.
You need to pass on collection_name, manufacturer, description and image_url in your context.
context = {
'products': products,
'collection_name': collection_name,
'manufacturer': manufacturer,
'description': description,
'image_url': image_url
}
Now you can access these in your template like:
{% for instance in collection_name %}
{{ instance }} Collection <br />
{% endfor %}
Same for the others.
It should render only one object in the loop. Still you can use first in your interpolation.
Like this:
{{ instance.collection_name|first }}
EDIT
You need to pass collection_name as you have initialised it as an empty list therefore it is a variable which you can use only when you pass it in context.
context={'products': products, 'collection_name': collection_name}

Filtering field in DJango in the URL and in the view

Lets say I have a model with 2 fields:
class Bands(models.Model):
id = models.IntegerField(db_column='ID', primary_key=True)
name = models.CharField(db_column='NAME')
type = models.CharField(db_column='TYPE')
...
What I want to do is to list all the fields data with just one template. For example:
{% block content %}
{{ field_name }}
<ul>
{% for band in bands %}
<li>{{ band }}</li>
{% endfor %}
</ul>
{% endblock %}
So, how should I make my url?
url(r'^band/$', views.band, name='band')
or
url(r'^band/(?P<band>\w+)/$', views.band, name='band')
The link to that page would be:
Name
In the view I'm taking the values as this:
def band(request, field):
results = Results.objects.all()
names = [n.name for n in results]
types = [t.type for t in results]
if field == 'name':
bands = names
else:
bands = types
return render(request, 'band.html', {'bands': bands, 'field_name': field})
Is this the right way to do this (in the view and the url)? Thanks in advance.
Well, the simplest thing to do is use the DetailView.
from .models import Band
class BandDetailView(DetailView):
model = Band
And in urls.py something like:
from band.views import BandDetailView
url(r'^band/(?P<pk>\d+)/?$', BandDetailView.as_view(), name='band-detail')
And in the template:
{% url 'band-detail' pk=1 %}
That said, your model doesn't make much sense to me, as does the Led Zeppelin vs. Deep Purple bits in the view. Can you explain your project / need a bit more?

Django field value not showing in the template

I have the following query but when I loop over in my template it doesn't show the name field value.
views.py
hashtags = PhotoHashtag.objects.values('hashtag__name')\
.filter(hashtag__hashtagtype_id=3)\
.annotate(total_photos=Count('photo_id'))\
.order_by('-total_photos')
models.py
class PhotoHashtag(TimeStampedModel):
photo = models.ForeignKey('posts.Photo')
hashtag = models.ForeignKey('hashtags.Hashtag')
class Hashtag(TimeStampedModel):
hashtagtype = models.ForeignKey('hashtags.HashtagType')
name = models.CharField(max_length=250, unique=True)
template
{% for hashtag in hashtags %}
<li>({{ hashtag.name }}) ({{ hashtag.total_photos }})</li>
{% endfor %}
sql result
html
() (5)
() (2)
As you can see it shows the total_photos value but not the name value
You don't have a queryset of Hashtag objects. You have a ValuesQuerySet, created from the PhotoHashtag model, with two fields, hashtag__name and total_photos.
QuerySet.values returns a dictionary-like object ValuesQuerySet. This is why you can filter it.
The reason {{ hashtag.total_photos }} works is because 'total_photos' is a key in the resulting ValuesQuerySet object. Other keys in there are 'hashtag__name' and 'total_photos'. You can access them directly either by dot notation or like in a dict object if you're using something like Jinja2.
To make this work, just replace what you have with this:
{% for hashtag in hashtags %}
<li>({{ hashtag.hashtag__name }}) ({{ hashtag.total_photos }})</li>
{% endfor %}

Determine variable type within django template

I have a variable that I'm pulling into a table that sometimes is a date and sometimes is a string. If the variable is a date, I want to change the formatting:
<td>{{ action.extra_column|date:"M d" }}</td>
But if it is a string, I just want to display it as is:
<td>{{ action.extra_column }}</td>
If I try to format it and it is a string, I get no output for the variable.
How can I determine the type so that I can adjust my rendering based on type.
You could also implement a general template filter as an equivalent to the type() builtin:
# app/templatetags/util.py
from django import template
register = template.Library()
#register.filter
def get_type(value):
return type(value)
# template.html
{% load util %}
{% if extra_column|get_type == 'str' %}
String
{% elif extra_column|get_type == 'datetime.date' %}
Date
{% else %}
Oh no!
{% endif %}
I think Ignacio and Dirk are right, however. Can't you just have two keys (you say "array", but I assume you mean "dictionary" from the fact that the items have names) called date and detail?
# views.py
...
actions = [{
'some_property': 'some_value'
'date': None,
'detail': 'details'
},
{
'some_property': 'some_value'
'date': datetime.date.today(),
'detail': None
}]
...
# template.html
{% for action in actions %}
<td>{% if action.date %}{{ action.date|date:"M d" }}{% endif %}{{ action.detail }}</td>
{% endfor %}
# output
<td>details</td>
<td>Aug 19</td>
Like Ignacio Vazquez-Abrams pointed out in the first comment, that's not really a great way to code your logic. I would ensure that your variable has a certain type. That could be solved through an additional variable you add to the context or an object that holds the data and something that describes the type of data.
If you want to stick to your logic, a possible approach would be to write your own template filter (let's call it date_or_string). The filter could subclass the builtin date filter with the format parameter being optional. In case the parameter is passed it works like the normal date filter, without the parameter it simply returns the string. In a more complex scenario the filter could also do some type checking. Just an idea, i wouldn't actually put that kind of logic into the template.
I know I'm way behind on this (by three years) but I just got here looking to do something similar and came up with what I think is a decent solution.
Just add a function to your models like get_model_type and have it return something you'd expect from each model like so:
class MyModelOne(models.Model):
date_created = models.DateTimeField(auto_now_add=True)
first_name = models.CharField(max_length=255)
def get_model_type(self):
return "my_model_one"
class MyModelTwo(models.Model):
date_created = models.DateTimeField(auto_now_add=True)
other_field = models.CharField(max_length=255)
def get_model_type(self):
return "my_model_two"
Then in your template you can easily just call that function:
{% if model.get_model_type == 'my_model_one' %}
<p>Model One</p>
{% elif model.get_model_type == 'my_model_two' %}
<p>Model Two</p>
{% endif %}
Late to the party, but I just had this problem. The solution I went for is duck-typing, so:
{% if action.extra_column.year %}
{{ action.extra_column|date:"M y" }}
{% else %}
{{ action.extra_column }}
{% endif %}
Could you argue that this is definitely not the right way to do it? Probably. Will it get the job done without writing your own template filter and having even more code to maintain? Absolutely.
That's my approach:
#register.filter
def get_type(value):
""" It returns variable type as a pure string name """
return type(value).__name__
You can try this to recognize String vs List type:
{%if v_1.0.1|length == 0%}
<!--STR-->
{{v_1}}
{%else%}
<!--List-->
{{v_1.0}}
{%endif%}

django templatetags not return correct data

I've built a small templatetag that looks towards my DB and makes a calculation based on the most popular trophies logged.
templatetag looks as follows:
#register.inclusion_tag('trophies/trophies.html')
def trophies():
return { 'trophies': Trophies.objects.values("specie").annotate(Count("id")).order_by()}
trophies/trophies.html
{% for obj in trophies %}
<li>{{ obj.specie }}</li>
{% endfor %}
trophy model
class Trophies(models.Model):
user = models.ForeignKey(User)
specie = models.ForeignKey(Specie)
Specie model
class Specie(ImageModel):
species = models.CharField(max_length=50, unique=True, verbose_name='Common Name')
running {{ obj.specie }} returns the id, and running {{ obj.specie.species }} returns nothing.
Why does this happen?
Try this:
#register.inclusion_tag('trophies/trophies.html')
def trophies():
return { 'trophies': Trophies.objects.values("specie", "specie__species").annotate(Count("id")).order_by()}
And in template:
{{ obj.specie__species }}
See related question: Display Django values() on Foreign Key in template as object instead of its id