Reputation: 43
Everybody tells that it is very good practice to use get_absolute_url in templates. But in my case it causes lots of the same queries to the database on the single page. Here is structure of urls I have to develop (I can't change it because the customer has already working web-site, and Google is not going to like it if I change urls) - mysite/category/subcategory/product_slug.html Here is the code of url patterns:
from django.conf.urls import url
from . import views
urlpatterns = [
url(r'^(?P<parent_category_slug>[\w-]+)/(?P<category_slug>[\w-]+)/(?P<slug>[\w-]+)\.html$', views.ProductDetailView.as_view(), name='product_detail'),
url(r'^(?P<parent_slug>[\w-]+)/(?P<slug>[\w-]+)$', views.ProductListView.as_view(), name='products_of_category'),
url(r'^(?P<slug>\w+)$', views.SubCategoryListView.as_view(), name='sub_category'),
url(r'^$', views.CatalogIndexListView.as_view(), name='index'),
]
And here is code of get_absolute_url in Product model:
def get_absolute_url(self):
return reverse('product_detail', kwargs={'slug':self.slug, 'parent_category_slug':self.product_category.category_parent.slug,
'category_slug':self.product_category.slug})
So, when I go at mysite/category/subcategory, I see all products belong to the subcategory. This is a list (actually table, with images, titles and so on). And all the images as well as titles must be urls to the product. Here is piece of code in template
e {% for product in products %}
<tr>
<td class="product_name_list">
<a href="{{ product.get_absolute_url }}">{{ product.product_name }}</a>
</td>
<td class="product_article_list">{{ product.product_article }}</td>
{% if product.product_main_image %}
<td class="product_image_list"><a href="{{ product.get_absolute_url }}" ><img src='{{ product.product_main_image.url}}' alt=""></a></td>
{% else %}
<td class="product_image_list"><a href="{{ product.get_absolute_url }}" ><img src='{% static "images/empty.gif" %}' alt=""></a></td>
{% endif %}
<td class="product_request_list"><a href="#">Запросить</a></td>
</tr>
{% endfor %}
So, in result, I have really a lot of queries to database, because get_absolute_url is called repeatedly.
Please, help me to avoid this. I've tried to set default Manager class with 'get_related()', but it was stupid, obviusly it did not help, because every instanse calls method get_absolute_url again and again.
Thanks in advance!
Upvotes: 1
Views: 483
Reputation: 11
Decorating get_absolute_url()
as a @cached_property
doesn't work if this method is called from Python code – a property must not be written in call syntax (with parentheses). In templates this doesn't matter as there are no parentheses anyway. The following suggestion caches the URL but requires no further changes, neither in views nor in templates:
@cached_property
def absolute_url(self):
return reverse(...)
def get_absolute_url(self):
return self.absolute_url
Should the URL ever change on an existing object, the cached value can be cleared with del obj.absolute_url
.
Upvotes: 1
Reputation: 8250
You can use django's cached_property
decorator to solve this
from django.utils.functional import cached_property
# You can either use it convert `get_abolute_url` method to property
@cached_property
def get_absolute_url(self):
return reverse(
'product_detail', kwargs={
'slug':self.slug,
'parent_category_slug':self.product_category.category_parent.slug,
'category_slug':self.product_category.slug})
# or decorate the method with different name so that you can use both
cached_absolute_url = cached_property(get_absolute_url)
By this you'll be able to use both,
object.get_absolute_url()
object.cached_absolute_url
cached_property
caches the value of the method so that when you call it again, instead of running through the whole method, it directly returns you the cached value.
Upvotes: 2