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 ?
Related
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),
)
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.
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.
I'm playing with django-haystack and trying to implement it with elasticsearch (I already did actually). My model has title, content and also a tags field which is a ManyRelatedManager:
tags = models.ManyToManyField( 'Tag', through = 'PostTags' )
My search index object is built as follows:
class PostIndex( indexes.SearchIndex, indexes.Indexable ):
text = indexes.CharField( document = True, use_template = True )
title = indexes.CharField( model_attr = 'title', boost = 1.125 )
content = indexes.CharField( model_attr = 'content' )
date_added = indexes.DateTimeField( model_attr = 'date_added' )
My first question is...how do I include the tags in the PostIndex object? I want to give the tags a much higher boost compared to the title and content.
post_text.txt template:
{{ object.title }}
{{ object.content }}
{% for tag in object.tags.all %}
{{ tag.name }}
{% endfor %}
You could add a multi facet field for the tags and populate & boost that at indexing time.
In your PostIndex model:
tags = FacetMultiValueField(boost = 2)
and
def prepare_tags(self, obj):
return [t.name for t in obj.tags.all()]
I have three models as
class Category(models.Model):
name = models.CharField(max_length=128)
class SubCategory(models.Model):
category = models.ForeignKey(Category)
name = models.CharField(max_length = 400)
class Document(models.Model):
category = models.ForeignKey(Category, null=True,blank=True,help_text=_('Required'))
subcategory = models.ForeignKey(SubCategory, null=True, blank=True, help_text =_('Required'))
title = models.CharField(max_length=300)
Now in Admin interface I have category, subcategory and title field.
If a user is trying to select any subcategory then only that
subcategory item should be displayed which are related to Category.
A simple example is Country, state dropdown.
I am trying to get it from Modelform like
class DocumentAdminModelForm(ModelForm):
def __init__(self, *args, **kwargs):
super(DocumentAdminModelForm, self).__init__(*args, **kwargs)
self.fields['sub_category'] = forms.ModelChoiceField(queryset = SubCategory.objects.filter(category__id = self.fields['category'] ))
but it is not working. Should I use some ajax and jquery for this or
is there any other way you can suggest ??
Thanks
It seems that is the best way to realize this relations, one model with parent relation.
Here is my solution which is based on native django fields,templatetags and little custom of admin template.
In example, i generate custom select element with tabbed childs (based on native django).
Example of select field (sorry, example in Russian language):
Realization of Select class(for edit and create):
class mSelect(Widget):
def __init__(self, attrs=None, choices=()):
super(mSelect, self).__init__(attrs)
# choices can be any iterable, but we may need to render this widget
# multiple times. Thus, collapse it into a list so it can be consumed
# more than once.
self.choices = list(choices)
def render(self, name, value, attrs=None, choices=()):
if value is None: value = ''
final_attrs = self.build_attrs(attrs, name=name)
print name
output = [u'<select name=\"%s\" style=\"width:200px;\">' % name]
output.append(u"<option value=\"\"%s>----------</option>")
options = self.render_options(choices, [value])
if options:
output.append(options)
output.append('</select>')
return mark_safe(u'\n'.join(output))
def render_options(self, choices, selected_choices):
def render_option(option_value, option_label):
option_value = force_unicode(option_value)
selected_html = (option_value in selected_choices) and u' selected="selected"' or u''
return u'<option value="%s"%s style=\"padding-left:20px;\"> %s</option>' % (
escape(option_value), selected_html,
conditional_escape(force_unicode(option_label)))
# Normalize to strings.
selected_choices = set([force_unicode(v) for v in selected_choices])
output = []
for option_value, option_label in chain(self.choices, choices):
childs = **YOUR_MODEL**.objects.filter(parent=option_value).order_by("**YOUR_FIELDNAME**")
if len(childs)>0:
output.append("<option value=\"%s\" disabled >%s</option>" % (option_value,option_label))
for child in childs:
output.append(render_option(child.id, child.iname))
return u'\n'.join(output)
Then you need to create modelAdmin class for you model:
Example:
class **YOUMODELADMIN**(admin.ModelAdmin):
.....
.....
.....
def formfield_for_dbfield(self, db_field, **kwargs):
if db_field.name == '**YOUR_FIELD_NAME**':
kwargs["widget"] = mSelect()
field = super(**YOUMODELADMIN**, self).formfield_for_dbfield(db_field, **kwargs)
return field
If you need to show this relations in admin(list_filter) i think the best way is to write templatetag for this field+javascript function to show relation tree.
Example(image+code):
(copy file change_list.html into your template folder like: templates/admin/App/model or yourappname/templates/admin/yourmodelname/ change_list.html
Then add call of your template tag in list filter block:
Example of Javascript block:
<script>
function showTree(name_id)
{
if(document.getElementById("li_" + name_id).style.display=="none")
{
//document.getElementById("div_" + name_id).style.display = "block";
document.getElementById("li_" + name_id).style.display = "block";
}
else
{
//document.getElementById("div_" + name_id).style.display = "none";
document.getElementById("li_" + name_id).style.display = "none";
}
}
</script>
Example code of templatetag(python):
def YOURTEMPLATETAG(request):
root_link = "/admin/YOURAPP/YOURMODEL/"
mvar = "YOURFIELD_ID__id__in"
mlink = root_link + "?"
for item in request.GET:
if item!=mvar:
mlink += "&%s=%s" % (item,request.GET[item])
arr_options = []
dest = HERE YOU GET YOUR ROOT OBJECTS
selected = ""
for item in dest:
show = False
childs = HERE YOU GET YOU CHILD OBJECTS
if len(childs)>0:
str_req = "".join("%d," % child.id for child in childs).rstrip(",")
if u"ptype__id__in" in request.GET:
selected = request.GET["YOURFIELDNAME__id__in"]
if selected in str_req.split(","):
show = True
proot = {"name":item.iname,"link":str_req,"childs":childs,"show":show}
arr_options.append(proot)
if "," not in selected and len(selected)>0:
selected = int(selected)
return render_to_string("PATH_TO_YOUR_TEMPLATETAG_TEMPLATE/templatetags/show_options.html",{"mlink":mlink,"selected":selected,"options":arr_options,"name":u"YOUR FILTER NAME","tst":request})
Example of template for templatetag:
<h3>{{name}}</h3>
<ul>
<!--li class="selected"?q=-->
<li{% ifequal selected '' %} class="selected"{% endifequal %}>Все</li>
{% for item in options %}
<li {% ifequal selected item.link %} class="selected"{% endifequal %} >
{{item.name}}
[show tree]
</li>
<li id="li_{{item.link}}" {% ifequal item.show 1 %}{%else%}style="display:none;"{%endifequal%}>
<ul>
{% for child in item.childs %}
<li {% ifequal selected child.id %} class="selected"{% endifequal %}><div style="margin-left:10px;">{{child.FIELDOFNAME}}</div></li>
{% endfor %}
</ul>
</li>
{% endfor %}
</ul>
And finally block for change_list.html:
....
....
<div id="changelist-filter" style="width:350px;z-index:0;">
{% load YOURTEMPLATETAGFILE %}
{% show_date_cal request "/PATH_TO_YOUR_MODEL_VIEW/" %}
<h2>Custom filters</h2>
{% TEMPLATETAGNAME request %}
<h2>{% trans 'Filter' %}</h2>
{% for spec in cl.filter_specs %}{% admin_list_filter cl spec %}{% endfor %}
...
.....
I think anyway this example will be useful for creating custom controls+admin filters
Sorry if not )))