How to iterate in subitems of a object in a django template - django

views.py:
from django.views import generic
from .models import Servico
class ServicoView(generic.DetailView):
model = Servico
context_object_name = 'servico'
template_name = 'servico.html'
models.py:
from djongo import models
class PublicoAlvo(models.Model):
def __str__(self):
return ''
alvo1 = models.CharField(max_length = 126)
alvo2 = models.CharField(max_length = 126, blank = True, default = '')
class Meta:
abstract = True
class Servico(models.Model):
t_id = models.CharField(primary_key = True, unique = True, max_length = 252)
alvos = models.EmbeddedField(
model_container = PublicoAlvo
)
urls.py:
from django.urls import path
from . import views
urlpatterns = [
path('servicos/<slug:pk>/', views.ServicoView.as_view(), name = 'servico')
]
I think these are the relevant files in my Django app folder. Back to the question, how can I iterate over the values that are going to be stored in servico.alvos in my template? If I want to show t_id, I just use {{ servico.t_id }} and it works fine. I could write something like:
<HTML1>
{{ servico.alvos.alvo1 }}
<HTML2>
<HTML1>
{{ servico.alvos.alvo2 }}
<HTML2>
And that would show the values that I want, but would make things uglier, since I would have to write a lot of repeated standard HTML (that I indicated as and ) to format each value inside servico.alvos, and more limited (imagine if I decide to change the model and add more 6 values in the PublicoAlvo class). I tried the following:
{% for alvo in servico.alvos.all %}
<HTML1>
{{ alvo }}
<HTML2>
{% endfor %}
and
{% for alvo in servico.alvos.items %}
<HTML1>
{{ alvo }}
<HTML2>
{% endfor %}
But I get nothing printed. When I try:
{% for alvo in servico.alvos %}
<HTML1>
{{ alvo }}
<HTML2>
{% endfor %}
I get 'PublicoAlvo' object is not iterable
Is there a way to get what I want using a loop in my template or changing something in my models.py?

Try
{{ servico.t_id }}
{{ servico.alvos }}
Then in your models.py
class PublicoAlvo(models.Model):
def __str__(self):
# Option 1: List all <alvo> fields
return ", ".join(
[
self.alvo1,
self.alvo2,
]
)
# Option 2: If you don't like manually listing each <alvo> field
# return ", ".join(
# [
# getattr(self, f"alvo{index}") for index in range(1, 3)
# ]
# )
...
This might give something like
The value for alvo1, While for alvo2 is this one
Update
You could also try
{% for key, value in servico.alvos.items %}
{% if key|slice:":4" == "alvo" %}
{{ value }}<br>
{% endif %}
{% endfor %}
This might show something like
The value for alvo1
While for alvo2 is this one

Based on the first answer of Niel Godfrey Ponciano, I was able to solve the problem.
models.py:
from djongo import models
class PublicoAlvo(models.Model):
def __str__(self):
return ''
def list(self):
return [value for key, value in self.__dict__.items() if not key.startswith('_')]
alvo1 = models.CharField(max_length = 126)
alvo2 = models.CharField(max_length = 126, blank = True, default = '')
class Meta:
abstract = True
class Servico(models.Model):
t_id = models.CharField(primary_key = True, unique = True, max_length = 252)
alvos = models.EmbeddedField(
model_container = PublicoAlvo
)
And then I can iterate over servico.alvos.list using a for in the template just by adding the list method that returns the relevant fields (variables) values in my class.

Related

List of child ID in django template

Here is my models.py:
#child
class Country(models.Model):
name = models.CharField(max_length=255)
wine_rg = models.ManyToManyField(WineRegion, blank=True)
#parent
class WorldRegion(models.Model):
name = models.CharField(max_length=255)
country = models.ManyToManyField(Country, blank=True)
def __str__(self):
return self.name
views.py:
world_region_filters = WorldRegion.objects.all()
templates/test.html:
{% for world_region in world_region_filters %}
{{ world_region.name }} - {{ return list of country ID }}
{% endfor %}
How to return all country ID (child) on django template? I know I can do this:
{% for country in world_region.country.all %} {{ country.id }} {% endfor %}
But is there any way to make it shorter? I've tried this:
{{ world_region.country.all.id }}
But it doesn't work. Any suggestions?
i dont know why you want a list of ids in html
whatever you've implemented is fine still if you want list of ids maybe you can do like this:
data = WorldRegion.objects.all().values("name", "country_id")
region_names = [ i['name'] for i in data ]
country_ids = [ i['country_id'] for i in data ]
then you can pass it to html as a context
If you are using PostgresSQL, then you can use ArrayAgg like this:
from django.contrib.postgres.aggregates.general import ArrayAgg
from django.db.models import Count
world_region_filters = WorldRegion.objects.all().annotate(
country_list=ArrayAgg('country', distinct=True),
)

haystack search not indexing multivaluefield django

I am trying to use manytomanyfield for fetch searching but when I am trying to update index, it shows no attribute error
#search_index.py
class ProductCreateModelIndex(indexes.SearchIndex, indexes.Indexable):
text = indexes.CharField(document=True, use_template=True,template_name='search/indexes/catalogue/product_text.txt')
user = indexes.CharField(model_attr='user',faceted=True )
title = indexes.EdgeNgramField(model_attr='title')
description = indexes.CharField(model_attr='description',null = True)
category = indexes.CharField(model_attr='category',faceted=True )
brand = indexes.CharField(model_attr='brand_name',faceted=True )
availability_regions = indexes.MultiValueField(faceted=True )
model = indexes.EdgeNgramField(model_attr='model_name',null = True)
def prepare_availability_regions(self, obj):
return [ availability_regions.name for a in obj.availability_regions_set.all()]
when I am trying to update it shows me this
return [ availability_regions.name for a in obj.availability_regions_set.all()]
AttributeError: 'ProductCreateModel' object has no attribute 'availability_regions_set'
here is my product_text.txt
{{object.title}}
{% for a in availability_regions %}
{{ a }}
{% endfor %}
{{object.category}}
{{object.model_name}}
{{object.brand_name}}
{{ object.description|default:"" }}
I just follow the tutoria, I don't know how to do it in right way, would you tell me the right way to do it and where can i find the documentation of this ?

Django prefetch related no duplicates with intermediate table

I have a question I am trying to solve for one day now.
With the models
class Quote(models.Model):
text = models.TextField()
source = models.ForeignKey(Source)
tags = models.ManyToManyField(Tag)
...
class Source(models.Model):
title = models.CharField(max_length=100)
...
class Tag(models.Model):
name = models.CharField(max_length=30,unique=True)
slug = models.SlugField(max_length=40,unique=True)
...
I am trying to model the world of quotes. with relationships: one Source having many Quotes, one Quote having many Tags.
Problem is:
How do I get all Tags that are contained in a Source (through the contained Quotes) ?
with the minimum possible queries.
with the amount of times they are contained in that source
I have tried the naive one with no prefetch related, with a model method
def source_tags(self):
tags = Tag.objects.filter(quote__source__id=self.id).distinct().annotate(usage_count=Count('quote'))
return sorted(tags, key=lambda tag:-tag.usage_count)
And in the template:
{% for tag in source.source_tags|slice:":5" %}
source.quote
{% endfor %}
Now I have
sources = Source.objects.all().prefetch_related('quote_set__tags')
And in the template I have no idea how to iterate correctly to get the Tags for one source, and how I would go about counting them instead of listing duplicate tags.
This will get the result in a single SQL query:
# views.py
from django.db.models import Count
from .models import Source
def get_tag_count():
"""
Returns the count of tags associated with each source
"""
sources = Source.objects.annotate(tag_count=Count('quote__tags')) \
.values('title', 'quote__tags__name', 'tag_count') \
.order_by('title')
# Groupe the results as
# {source: {tag: count}}
grouped = {}
for source in sources:
title = source['title']
tag = source['quote__tags__name']
count = source['tag_count']
if not title in grouped:
grouped[title] = {}
grouped[title][tag] = count
return grouped
# in template.html
{% for source, tags in sources.items %}
<h3>{{ source }}</h3>
{% for tag, count in tags.items %}
{% if tag %}
<p>{{ tag }} : {{ count }}</p>
{% endif %}
{% endfor %}
{% endfor %}
Complementary tests :)
# tests.py
from django.test import TestCase
from .models import Source, Tag, Quote
from .views import get_tag_count
class SourceTags(TestCase):
def setUp(self):
abc = Source.objects.create(title='ABC')
xyz = Source.objects.create(title='XYZ')
inspire = Tag.objects.create(name='Inspire', slug='inspire')
lol = Tag.objects.create(name='lol', slug='lol')
q1 = Quote.objects.create(text='I am inspired foo', source=abc)
q2 = Quote.objects.create(text='I am inspired bar', source=abc)
q3 = Quote.objects.create(text='I am lol bar', source=abc)
q1.tags = [inspire]
q2.tags = [inspire]
q3.tags = [inspire, lol]
q1.save(), q2.save(), q3.save()
def test_count(self):
# Ensure that only 1 SQL query is done
with self.assertNumQueries(1):
sources = get_tag_count()
self.assertEqual(sources['ABC']['Inspire'], 3)
self.assertEqual(sources['ABC']['lol'], 1)
I have basically used the annotate and values functions from the ORM. They are very powerful because they automatically perform the joins. They are also very efficient because they hit the database only once, and return only those fields which are specified.

Add custom html to choicefield label in django

I am struggling with a requirement now, I want to add an image to choice field label and I really dont have a clue how to do it. I am using django form wizard to render the form.
Here is the image which shows what I want to achieve :
And here is what I have got right now ( to make the radio buttons inline, I know it could be achieved through css):
Here is the forms.py:
from django import forms
from django.utils.translation import gettext as _
CHOICES=[('0','Pay by card'), ('1','Invoice')]
class PaymentForm(forms.Form):
title = 'payment'
payment_method = forms.ChoiceField(label = _("Payment Options"), choices=CHOICES, widget=forms.RadioSelect(), required = True)
I am rendering using wizard form:
{{ wizard.form.payment_method.label_tag }}
{{ wizard.form.payment_method|safe }}
{{ wizard.form.payment.errors}}
Anyone has any suggestion for this apart from custom widget?
Without a widget:
from django.utils.safestring import mark_safe
CHOICES=[('0', mark_safe('Pay by card <img src="by_card.jpg"/>')),
('1', mark_safe('Invoice <img src="no_card.jpg"/>'))
]
Credit: setting help_text for each choice in a RadioSelect
1) Shortly (but i'm not sure) call a function where 'Pay by card' and return all <img>... that you need.
2) You can make somthing like #Gahbu said
3)Long [Better, i think, but untested :( ]:
Make a renderer:
from myapp.my_widgets import CardsRadioFieldRenderer
CARD_CHOICE = '0'
CHOICES=[(CARD_CHOICE,'Pay by card'), ('1','Invoice')]
class PaymentForm(forms.Form):
title = 'payment'
payment_method = forms.ChoiceField(label = _("Payment Options"), choices=CHOICES,widget=forms.RadioSelect(renderer=CardsRadioFieldRenderer), required = True)
# myapp/my_widgets.py
class CardRadioInput(RadioInput):
def __init__(self, name, value, attrs, choice, index):
self.name, self.value = name, value
self.attrs = attrs
choice_value = force_text(choice[0])
self.choice_value = choice_value
if choice_value == CARD_CHOICE:
choice_label = force_text(self.get_html_for_card_choice())
else:
choice_label = force_text(choice[1])
self.choice_label = choice_label
self.index = index
def get_html_for_card_choice(self):
#some logic to get the images tags (<img ...> <img ...>)
return text
class CardsRadioFieldRenderer(RadioFieldRenderer):
def __getitem__(self, idx):
choice = self.choices[idx] # Let the IndexError propogate
return CardRadioInput(self.name, self.value, self.attrs.copy(), choice, idx)
In your template do something like :
{% for choice in wizard.form.payment_method.choices %}
{{ choice.0 }} {# value #} {{ choice.1 }} {# value #}
{% if choice.0 == PAYMENT_BY_PAYPAL %}
...
{% endif %}
{% endfor %}
You can also write :
{% for key, value in wizard.form.payment_method.choices %}
{% if key == PAYMENT_BY_PAYPAL %}
...
{% endif %}
{% endfor %}
If you want to tweak it from the template, you need to use the "safe" which tells Jinja not to escape it. like below
{{ some_object.some_property|safe }}
Yet if you want to use it inside your python code while defining the form/modelform just use mark_safe as below.
mark_up = 'this is a link'
choices = list()
choices.append(('some_id', mark_safe(mark_up)))
self.fields['some_field'].choices = choices

Django tagging select_related

I using Django tagging project.
That is was very stable project.
Working on django 1.3.
But i have a problem.
# in models.py
from tagging.fields import TagField
class Blog(models.Model):
title = models.CharField(max_length = 300)
content = models.TextField()
tags = TagField()
author = models.ForeignKey(User)
# in views.py
def blog_list(request):
# I Want to use select related with tags
blogs = Blog.objects.all().select_related("user", "tags") # ????
....
# in Templates
{% load tagging_tags %}
{% for blog in blogs %}
{% tags_for_object blog as tags %}
{{blog.title}}
{% for tag in tags %}
{{tag}}
{% endfor %}
{% endfor %}
django-tagging uses a generic foreign key to your model, so you can't just use select_related.
Something like this should do the trick though:
from django.contrib.contenttypes.models import ContentType
from collections import defaultdict
from tagging.models import TaggedItem
def populate_tags_for_queryset(queryset):
ctype = ContentType.objects.get_for_model(queryset.model)
tagitems = TaggedItem.objects.filter(
content_type=ctype,
object_id__in=queryset.values_list('pk', flat=True),
)
tagitems = tagitems.select_related('tag')
tags_map = defaultdict(list)
for tagitem in tagitems:
tags_map[tagitem.object_id].append(tagitem.tag)
for obj in queryset:
obj.cached_tags = tags_map[obj.pk]
I have recently encountered the same problem and solved it without a single db query. Indeed, we don't need tag id to get url. As tag name is unique and is db_index you can get url using name field instead of id. E.g.
# your_app/urls.py
url(r'tag/(?P<tag_name>[-\w]+)$', tag_detail_view, name='tag_detail')
Also, tagging TagField gives us a string with tag names, like 'python,django'. So we can write a custom template filter:
# your_app/templatetags/custom_tags.py
from django.urls import reverse
#register.filter
def make_tag_links(tags_str):
return ', '.join([u'%s' % (reverse(
'tag_detail', args=[x]), x) for x in tags_str.split(',')])
And then you can write in template:
# your_list_template.html
{% for blog in blogs %}
{{blog.title}}
{% if blog.tags %}
{{ blog.tags|make_tag_links|safe }}
{% endif %}
{% endfor %}