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 %}
Related
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!
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}
I have a MultipleChoiceField forms field (associated with a models CharField) which shows up in the database like this. It seems to have been converted to a string in the database, because when I try to display it with a 'for property in property_type' statement in the HTML, it shows up like this. I want it to be displayed like this
So I have to write some code to fix this issue. My pseudocode will look something like:
for record in property_type:
split record at comma
for i in record:
if record[i] is not a letter or number:
remove record[i]
Now my question is, where do I write this code? Do I write it in the views.py or in the HTML file? I tried doing it in views but I don't know how to select a single database record. I tried doing it in the HTML but I was limited by the template tags.
Here is the shortened version of my code:
models.py:
property_type = models.CharField(max_length=50, help_text="You can select more than 1 option")
forms.py:
property_type = forms.MultipleChoiceField(widget=forms.SelectMultiple, choices=BuyerListing.PROPERTY_TYPE)
HTML:
{% for property in listing.property_type %}
<p>Property type: {{ property }}</p>
{% endfor %}
EDIT:
Got it to work thanks to #ytsejam and #Daniel Roseman. With ytsejam's code, the result will show up like this:
['1' '2']
I added a basic regex to ytsejam's code to remove the brackets and quotes:
def split_properties(self):
a = self.property_type
a = re.sub(r'[\]\[\']', '', a)
a = a.split(',')
return a
Now the list will display like this, which is very easy to manipulate in HTML.
1 2
in your models.py
def split_properties(self):
return self.properties.split(',')
and in your template use
{% for property in className.split_properties %} {{ property }} {% endfor %}
My template renders the tag {{ test.date}} in the following format -
2015-12-15T23:55:33.422679
When I try to format it using django's built in template tag date, it doesn't display anything.
Variations I've tried:
{{ test.date|date:"SHORT_DATE_FORMAT" }}
{{ test.date|date:"D d M Y" }}
models.py:
class Human(models.Model):
name = models.CharField(max_length=50,default='',blank=False)
class Test(models.Model):
human = models.ForeignKey(Human)
date = models.DateTimeField(default=datetime.now)
views.py:
def list(request):
h = Human.objects.all()
s=[]
for her in h:
t = h.test_set.all()
s.extend(t)
context = RequestContext(request, {'test_list': s,})
return render_to_response('template.html', context)
I am then using it in template like this:
{% for test in test_list %}
{{test.date}}
{% endfor %}
What am I missing?
Answering an OLD post... but, the answer doesn't seem (to me) to be answering the original question - which was WHY isn't the Django inline template date formatting working...
The answer is (I believe) that the author was trying to output to his page something like:
"This is my date:{{test.date|date:"D d M Y"}}."
The problem, if this is truly what was being tried, is that the double quotes don't work in this situation. You need to do the following instead:
"This is my date:{{test.date|date:'D d M Y'}}."
Note the single quotes...
I'm not sure what you want from this logic, but I think you can use this:
def list(request):
test = Test.objects.all()
return render(request, 'template.html', {'test':test})
and in template:
{% for t in test %}
{% t.date %}
{% endfor %}
if you want display human, just add in cycle {% t.human.name %}
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