How should I understand below codes in Django? - django

1. define a class in the models.py which was created in my own app.
class Article(models.Model):
headline = models.CharField(null=True,blank=True,max_length=200)
content = models.TextField()
def __str__(self):
return self.headline
2. define a function in views.py
from firstapp.models import People,Article
def index(request):
article_list = Article.objects.all()
context = {}
context['article_list'] = article_list
index_page = render(request, 'first_web_2.html', context)
return index_page
The question is: Is the article_list a list?how should I understand "context['article_list'] = article_list"?

Variable article_list is a queryset, which is a collection of objects from your database stemming from the query Article.objects.all(). This particular query is much like SELECT * FROM Article.
The context is a dictionary where string 'article_list' is the key and the variable article_list is the value. The context is passed to your template via the render method where the key is used in your template to render the associated value.
Since you are passing a collection you would have to perform a loop on it in your template. For example, this would render an unordered list of headlines. Note the use of the dot operator to access headline.
<ul>
{% for a in article_list %}
<li> {{ a.headline}} </li>
{% endfor %}
</ul>

article_list is not a list, it's a QuerySet. QuerySets are representations of SQL queries via Django's Object Relational Mapper (ORM). It's easy to see them as lists but they are quite different. In any case, you should read Django's documentation about them.
As for context, you can think of it as passing variables that you can access in your templates. It could be strings, numbers, lists, QuerySets, dictionaries etc. In this case, you want to be able to access your all your Articles in the template, likely so that you can loop through them like {% for article in article_list %}. This allows you to then call the attributes like article.headline and article.content in your template.

QuerySet's in Django are iterable so article_list is not directly a list.
context['article_list'] = article_list
context is an dict and the entry 'article_list' is sign to the query result article_list.
In the template you can get access to the query set like
{% for article in article_list %}
{{ article }}
{% endfor %}

You question is hard to understand but I think I got the right answer. You need to read the framework documentation to understand "what that mean":
context: https://docs.djangoproject.com/en/2.0/ref/templates/api/#django.template.Context
queryset (the list) : https://docs.djangoproject.com/en/2.0/ref/models/querysets/#when-querysets-are-evaluated

Related

How to refer to set objects in a django view from a template?

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}

Django: context of modified query list

I have a database with blog style documents, i.e., author's name, publication date, body text, etc.
I have built a django framework to output entries from the database as a result of a search term. That part is ok. The problem is that I want to show sections of the body text with the matched search terms highlighted (equivalent to a google search result). This means that I can't create a template tag with the body_text attribute only, because that text is not highlighted. I already did a function that receives as input the query and the body text and outputs the same text with the found search terms in bold.
My problem now is how do I pass this result to the html template?
Using the tutorial from Django documentation suppose you have the following views.py:
def index(request):
latest_question_list = Question.objects.order_by('-pub_date')[:5]
context = {'latest_question_list': latest_question_list}
return render(request, 'polls/index.html', context)
and the correspondent template:
{% if latest_question_list %}
<ul>
{% for question in latest_question_list %}
<li>{{ question.question_text }}</li>
{% endfor %}
</ul>
{% else %}
<p>No polls are available.</p>
{% endif %}
Now suppose you have the function in views.py:
def signal_tokens(text,query_q):
...
return new_text
What should be the best way to replace the {{ question.question_text } with the output from signal_tokens? My solution was to replicate the context variable with a list of dictionaries, where each dictionary is a copy of each entry, except for the 'question_text' key, where I used signal_tokens result:
def index(request):
query_q = 'test'
latest_question_list = Question.objects.order_by('-pub_date')[:5]
new_context = []
for entry in latest_question_list:
temp_d = {}
temp_d['id'] = entry.id
temp_d['question_text'] = signal_tokens(entry.question_text,query_q)
new_context.append(temp_d)
context = {'latest_question_list': new_context}
return render(request, 'polls/index.html', context)
But the problem is that I need to copy all entries. Is there a more elegant way to solve this problem?
This is an ideal use case for a template filter. Move your highlight code to a file in the templatetags directory, register it as a filter, then you can call it from the template:
{{ question.question_text|highlight:query_q }}
Obviously you will need to pass query_q to the template context as well.

How to Pull info from another model within a DetailsView template (without ForeignKey)?

I am trying to pull data from two models within a DetailsView template (in Django). There is of course a primary model (eg. Articles) associated with the view, which is easy to access. However, I want to access data from a model (eg. Terms). I do not want to use ForeignKey because I will be using many 'Terms' in each 'Article,' and since ForeignKey will allow me to link to only row in the Terms model, I will have to set-up mutiple ForeignKey fields, which can get messy fast.
I was thinking that this could be accomplished using get_context_data or templatetags, but haven't have had any luck yet. Any thoughts?
From Django Documentation you can add any queryset or context value you like to call in your template context like book_list below will list all books and it doesn't has to be linked with any other models..
#views.py
class PublisherDetail(DetailView):
model = Publisher
def get_context_data(self, **kwargs):
# Call the base implementation first to get a context
context = super(PublisherDetail, self).get_context_data(**kwargs)
# Add in a QuerySet of all the books
context['book_list'] = Book.objects.all()
return context
#yourtemplate.html
{% for book in book_list %}
{% if book %}
{{ book.title }}
{% endif %}
{% empty %}
No book_list found.
{% endfor %}

loop in Django template: how to control the loop iterator?

I'm using Django to show a list of posts. Each post has a 'is_public' field, so if one post's 'is_public' equals to False, it should not be shown to the user. Also, I want to show a fixed number of posts in one page, but this number can be changing depending on views.
I decided to crop the queryset in template as a few views are using the same template, generating it in the view means a lot of repeated codes.
If written in python, it should look like this:
i=number_of_posts_to_show_in_one_page
while i:
if qs[i].is_public == True:
#show qs[i] to the page
i--
As the django template does not support while loop and for loop seems hard to control, is there a way of achieving this? Or should I do it in another way?(One idea is to crop the qs before looping)Thanks!
Update:
I've written this template tag to pre-process the queryset:
#register.simple_tag(takes_context=True)
def pre_process_list(context,list,numbers):
#if not user.has_perm('admin'):
context['result_list']=list.filter(is_public=True, is_removed=False)[0:numbers]
#else:
#context['result_list']=list[0:numbers]
return ''
Before using for loop in the template, I'll pass the queryset to this templage tag, and use a simple for loop to show its result.
If in the future I want to show non-public posts to admins(which is not decided yet), I can write in some logic like the commented ones, and have them styled differently in the template.
{% for post in posts %}
{% if post.is_public %}
{{ post }}
{% endif %}
{% endfor %}
Though this would be a perfect use case for a manager.
You could write a simple manager that filters public posts.
class PublicPostManager(models.Manager):
def get_query_set(self):
return super(PublicPostManager, self).get_query_set().filter(is_public=True)
Then you would add it to your Post Class:
class Post(models.Model):
...
public = PublicPostManager()
Then you could pass post.public.all() as public_posts to your template and simplify your loop:
{% for post in public_posts %}
{{ post }}
{% endfor %}
#arie has a good approach with the manager, but you can easily do the same without writing a manager:
# View
posts = Post.objects.filter(is_public=True) # or use the manager
# Now, you can either limit the number of posts you send
# posts = posts[:5] (only show five in the view)
return render_to_response('foo.html',{'posts':posts})
# Template
# Or you can do the limits in your template itself:
{% for post in posts|slice:":5" %}
{{ post }}
{% endfor %}
See the slice filter on more information.
However, since this is a common operation, with django 1.3 you can use class based views to automate most of this.

Django question: how can I get user specific information related to a domain object?

Here are my models:
class Activity(models.Model):
title = models.CharField(blank=False, max_length=100)
description = models.TextField(blank=False)
class UserActivityWork(models.Model):
activity = models.ForeignKey(Activity)
user = models.ForeignKey(User)
hours_worked = models.FloatField()
comment = models.TextField()
Example data would be, an Activity of "climbing Mt Everest" and each user would be able to input how long it took them and a comment.
Here's my question: How can I display a list of all the Activities, and if the user has entered data for that Activity, display the pertinent details next to the Activity?
So far, I have considered:
creating a dictionary of
UserActivityWork with a key of the Activity id and a value of the user's UserActivityWork. This would be fine with
me, but I have no idea of how to do
this in django's templating system (ie, how do you say: {{ user_work[activity.id] }})
creating an object that would hold
both the Activity and
UserActivityWork. I haven't done this
one, because I am hoping that django
has a better way to do this.
Any insight would be greatly appreciated!
Assuming you have 2 querysets accessable from within your template (say as activities and user_activities)
A naive way would be to iterate over each activity and then over each user activity.
{% for activity in activities %}
{{ activity.title }}
{% for user_activity in user_activities %}
{% ifequal user_activity.activity activity %}
Display userdata
{% endifequal %}
{% endfor %}
{% endfor %}
Dictionary lookups can be performed in templates by using a dot (.)
Technically, when the template system encounters a dot, it tries the following lookups, in this order:
Dictionary lookup
Attribute lookup
Method call
List-index lookup
Another option would be to create a custom template tag. You could loop over the activity list as before and then pass the activity and either the user_activity list or the user to the tag to perform the lookup and render the required data.
Thanks for the hint, Gerry. I found that writing a custom template tag as you suggested was the way to go.
Here are the gory details, in case anyone stumbles across this.
In the view method, I published a dictionary "user_activity_status" which contains a key of activity.id and value of UserActivityWork object for the logged in user's work on that activity
This is the the relevant section of the template. Basically this going to add a variable "map_value" with a value of
getattr(user_activity_status[activity.id], "comment")
Here's the template:
{% load *file-name-of-the-templatetag-file* %}
{% access_map_method user_activity_status activity.id comment %}
{% if map_value %}
{{ map_value }}
{% else %}
get working sucka!
{% endif %}
here is the section of the templatetag file (see Gerry's links for the details of how to set this up)
from django import template
register = template.Library()
#register.tag(name="access_map_method")
def do_access_map_method(parser, token):
try:
tag_name, dict_name , key_name, method_name = token.contents.split()
except ValueError:
msg = '%r tag requires three arguments' % token.contents[0]
raise template.TemplateSyntaxError(msg)
return MapNode(dict_name , key_name, method_name)
class MapNode(template.Node):
def __init__(self, dict_name, key_name, method_name):
self.dict_var = template.Variable(dict_name)
self.key_var = template.Variable(key_name)
self.method_name = method_name
def render(self, context):
try:
dict_obj = self.dict_var.resolve(context)
key_obj = self.key_var.resolve(context)
if key_obj in dict_obj.keys():
if self.method_name:
context['map_value'] = getattr(dict_obj[key_obj], self.method_name)
else:
context['map_value'] = dict_obj[key_obj]
else:
context['map_value'] = ''
except template.VariableDoesNotExist:
context['map_value'] = ''
return ''