Django ManyToManyField referencing self: Rendering to template - python

Below is the code for a project I'm working on. As you can see in the view, I'm running a query for objects in the Risk model that are associated with the primary key in the Url.
The problem is that I can't access the attributes of the object being queried.
Model
class Risk(models.Model):
risk_title = models.CharField(max_length=60)
root_causes = models.ManyToManyField('self',
through='Roots',
symmetrical=False,
related_name='root_cause')
class Roots(models.Model):
causal_risk = models.ForeignKey(Risk, related_name='causes', on_delete=models.CASCADE)
effected_risk = models.ForeignKey(Risk, related_name='effected_risk', on_delete=models.CASCADE)
View:
def view_risk(request, id):
try:
risk = Risk.objects.get(pk=id)
except Risk.DoesNotExist:
raise Http404("Risk does not exist")
roots = Roots.objects.filter(effected_risk=id)
args = {
'risk': risk,
'roots': roots,
}
return render(request, 'risks/risk_detail.html', args)
Template:
{% for root in roots %}
{{ root }}
{% endfor %}
Running the above code returns this:
Roots object (2) Roots object (3)
This should be expected, as these are the entries I've made to the model through the admin interface.
However, when I run:
{% for root in roots %}
{{ root.risk_title }}
{% endfor %}
The screen is left blank.
TLDR
Passing the query from the intermediary table to the template works, but the template can't access the object attributes. How can I run the above query and then access the objects in the template?
UPDATEDIncluded the render portion per JoJo's response. This problem has been solved per my other update.
Thank you in advance!

Note that the view function is missing the return statement, so there is actually no html page rendered at all.
Assuming that the template html is named 'my_template.html', the missing return should look something like:
def view_risk(request, id):
# ...
return render(request, 'my_template.html', args)
Further, as #JonHrovat mentioned, the template needs to call
{{ root.causal_risk.risk_title }}
within the for loop.
A note on efficiency:
If you call root.causal_risk.risk_title within the template with your current query for roots this leads to additional queries, one for each iteration. This can be avoided by using the select-related statement when querying for the roots in the view function:
roots = Roots.objects.select_related('causal_risk__risk_title').filter(effected_risk=id)

I forgot to query the direct attributes of the model. I got what I need with this template:
{% for root in roots %}
{{ root.causal_risk.risk_title }}
{% endfor %}

Related

how to use django model object in django templates?

I am not able to use the Django model object in Django templates. I want to iterate using the model user in the template and then play with the ActivityPeriod(model) of that user. Please check my code for the clarity:
Here is my code:
views.py
from .models import User,ActivityPeriod
def call_response(request):
user = User.objects.all()
return render(request, "Test/list.html", {"users":user ,"activityperiod":ActivityPeriod})
Test/list.html
{% for user in users %}
'real_name': {{ user.real_name}}},
'activity_periods': {% with activity=activityperiod.objects.get(id =user) %}
{{ activity.start_time }}
{% endwith %}
{% endfor %}
But i am getting an error:
Could not parse the remainder: '(id' from 'activityperiod.objects.get(id'
What is the correct way? Can anyone please share it with me.
Django template don't understand the filter action of Model. This part shoud be in view.
activity=activityperiod.objects.get(id =user)
You should prepare your data and manipulate them before sending to template (a dictionary may help you). And remember that result of action "User.objects.all()" is a list.
views.py
def call_response(request):
user = User.objects.filter(user=request.user)
activityperiod = activityperiod.objects.get(user=user)
context={'user':user,'activityperiod':activityperiod}
return render(request, "Test/list.html",context})
Test/list.html
'real_name': {{ user.real_name}}
'activity_periods':{{ activityperiod.start_time }}
Your question suggests that you think you can a function in the templates like a normal function (ie activityperiod.objects.get(...)).
You can't, the templating system is not made like this (for security reasons amongst others).
You should do something like, in your models:
def call_response(request):
# ! first() not "all()" (if > 1 user, you'll have problem)!
user = User.objects.first()
activityperiod = activityperiod.objects.get(user=user)
return render(request, "Test/list.html",
{"users":user ,"activityperiod":activityperiod})

Object is not iterable: Filtering QuerySet to display latest instance

I am trying to create a filter in a QuerySet using Django that returns the most recent instance that was submitted by the current logged in user.
I so far have the following in my view.py file:
def transfer(request):
title = 'Transfers'
queryset = Transfer.objects.filter(user=request.user).latest('timestamp')
context = {
"title": title,
"queryset": queryset,
}
if request.method == "POST":
print request.POST
return render(request, "transfer.html", context)
However this returns the error
TypeError: 'Transfer' object is not iterable
My models.py file looks like this so far:
from django.db import models
from django.contrib.auth.models import User
class Transfer(models.Model):
id = models.AutoField(primary_key=True)
user = models.ForeignKey(User)
amount = models.DecimalField(max_digits=10, decimal_places=2)
timestamp = models.DateTimeField(auto_now_add=True, auto_now=False)
and my html file is calling the QuerySet in the following way:
{% if request.user.is_authenticated %}
<h2>Your Transfers</h2>
{% if queryset %}
{% for instance in queryset %}
<p>Amount: {{ instance.amount }} </p>
<p>User: {{ instance.user }} </p>
<p>Date: {{ instance.timestamp }}</p>
{% endfor %}
{% else %}
<h3>You have not made any transfers.</h3>
{% endif %}
{% endif %}
Any suggestions would be hugely appreciated!
latest returns a single object: the latest one. So you're no longer passing an iterable queryset to the template, but a single instance which naturally you can't iterate.
It's not clear what you want to do here; perhaps you just need to remove the for loop, and just refer to the instance directly. Alternatively, if you do want differing you can iterate over, you might want to remove the latest call and just order by reverse timestamp.
Update you query like this
ueryset = Transfer.objects.filter(user=request.user).order_by('timestamp')[count_limit:]
[count_limit:] means how many objects from top you want to get. For getting top 5 you can do [:5]
You do not have to add id field, as django automatically add this.

Django SEO app, integrating external data like 'Best {{ product }} of the year'

I am trying to integrate external data in my metatags. What I want to achieve - by entering in Title field (in admin) "Details for {{ product.name }}" I would like to get to get automaticaly "Details for Hadron Collider" as a result in a browser. I have this in my template
{% load seo %}
{% get_metadata for product as metadata %}
{{ metadata.title }}
and I am passing Product object to the template, but what I get as a result is unchanged "Details for {{ product.name }}", so {{ value }} won't be populated / parsed? I have read the docs http://django-seo.readthedocs.org/en/latest/reference/administrators.html, it looks so simple
If you would like to reference the relevant model instance, you type the name of the model surrounded by two braces. For example Buy {{ product }} today!. You can go further and reference different fields from the relevant instance, for example By {{ product.name }} today!.
Though it doesn't work for me. Please, help. Probably I am doing something wrong.
Ok, after days of depression :) I solved it, at least for my project and currently only for Models, but for ModelInstance and Views it's almost I will make it later, it has the same problem in my case. I am quite new to Django, so a better solution may exist, I found this one.
I use Django 1.6.4, Python 2.7, multilingual project (Django Transmeta for model translation)
The problems:
1. variable substitution in models, model instances and views doesn't work. So, if we pass an object to DjangoSEO it won't substitute e.g. {{ product }}, {{ product.name }} variables. According to documentation it should. The problem, for Model is in backends.py, ModelBackend class. Function _resolve_value doesn't pass object to the function _resolve, which is supposed to populate meta tags with object properties. Here are the functions with slight changes that work for me.
def _resolve_value(self, name):
value = super(ModelMetadataBase, self)._resolve_value(name)
try:
return _resolve(value, self._content_object)
except AttributeError:
return value
def _resolve(value, model_instance=None, context=None):
""" Resolves any template references in the given value. """
if isinstance(value, basestring) and "{" in value:
if context is None:
context = Context()
if model_instance is not None:
context[model_instance.__class__.__name__.lower()] = model_instance
t = Template(value)
value = t.render(context)
return value
Also, in the file base.py, function get_linked_metadata we have to attach our object to Metadata instances like this:
if ModelMetadata is not None:
try:
model_md = ModelMetadata.objects.get(_content_type=content_type, _language=language)
except ModelMetadata.DoesNotExist:
model_md = ModelMetadata(_content_type=content_type, _language=language)
model_md._content_object = obj
instances.append(model_md)
And the problem 2 - DjangoSEO was using one Metadata instance for any language, even with option use_i18n = True. So, add _language=language as mentioned above.
Django SEO app is quite good, it has everything SEO needs (if it works :), so there is no reason to reinvent the wheel.
You need to wrap your variables in html tags.
{% load seo %}
{% get_metadata for product as metadata %}
<html>
<head>
<title>{{ metadata.title}}</title>
</head>
<body></body>
</html>

Making query from template

I am using jinja2 as the templating engine for django app. I wanted to make query in the template. I tried doing this:
{% for f in fs %}
{% Following.objects.filter(follows=f).count() %}
{% endfor %}
I'm passing 'fs' in variables while rendering the templates which a list.
But, its wrong. I can't do the 'query_set' call because of the way my models are defined. Here is a snippet:
class Following(models.Model):
user = models.ForeignKey(User)
follows = models.ForeignKey(F)
class F(models.Model):
name = models.CharField(max_length=50)
So, is there a possible way to do this?
If you are using jinja2 rather than the normal Django template language, what you have should work: you don't say why it doesn't.
But nevertheless, there's no need to define a separate method. You can use the automatic reverse relationship accessor:
{{ f.following_set.count() }}
You can't call methods that take parameter from django template. To overcome this, you can define method in your F model to get the required count.
For example:
class F(models.Model):
name = models.CharField(max_length=50)
def get_follow_count(self):
Following.objects.filter(follows=self).count()
Then in template you can do
{% for f in fs %}
{{ f.get_follow_count }}
{% endfor %}

Loading page in Django based on its ID

I've been working with Django in order to make my portfolio and I've managed to make a simple page manager. The problem is, it does not work how I want it to work:
I create the page.
It loads the content I gave it.
With jQuery, I load only that content (as formatted HTML).
It shows itself without reloading or moving to another page.
The problem is with the last two steps, I can't get the view and template to only load one.
Views.py:
def paginas(request, title):
get_page = Page.objects.all() # I can't think of a way to make a proper filter
return render_to_response('template.html', {'get_page': get_page}, context_instance=RequestContext(request), mimetype="text/html")
Template.html:
{% if get_page %}
{% for page in get_page %}
{{ page.content|safe }}
<p>Full path is {{ request.get_full_path }} and page id is {{ page.id }}</p>
{% endfor %}
{% else %}
<p>Nothing.</p>
{% endif %}
I know I should filter it, but I don't know how.
I appreciate your help.
tbh, the django tutorial explains urls, parameters and forms very clear, but here's the idea:
url(r'^/someapp/(?P<page_id>\d+)/$', paginas),
def paginas(request, **kwargs):
id = kwargs.pop('page_id')
page = get_object_or_404(Page, id=id)
# etcetera
class Page(models.Model):
# regular stuff
def get_absolute_url(self):
return "/someapp/%d/" % self.id
In paginas you are obviously getting all Pages.
To get one page you can use the get function
def paginas(request, title):
try:
your_page = Page.objects.get(title=title)
except Page.DoesNotExist:
# error no page for that title
# could use built in get_object_or_404 as ArgsKwargs suggested
It's also important to consider using a slug to make sure encoding is correct. The page id would be even better to use

Categories