How to send django model queryset to template through ajax? - python

Suppose I have model Abc and Tags which have many to many relation,
options = Abc.objects.all()
tagsset = []
for entry in options:
tags_arr = entry.tags_set.all()
if tags_arr:
tagsset.append(tags_arr)
data = {}
How do I format my queryset both options and tagsset in data?

You can put them in a dictionary, convert them to json, and them return the json_object
data = {}
data['options'] = options
data['tagsset'] = tagsset
json_object = json.dumps(data)
return HttpResponse(json_object)
This above code will send the json object to the calling ajax method

Simple answer:
data = {}
data['options'] = options
data['tagset'] = tagset
# NOTE: since you skip empty tag sets,
# len(tagset) <= len(options)
# so if you are going to match tagsets with options in the
# browser, they may not match length-wise
Although the question asked only about formatting the return parameters, this answer shows a different way to do the same (which is better IMO for cases where there is more data to be packed and sent. This approach also keeps related data together, i.e. options and related tags are binded together.
# payload is the data to be sent to the browser
payload = []
# get all options and associated tags
# this will be a list of tuples, where each tuple will be
# option, list[tags]
for option in Abc.objects.all():
payload.append((
option, # the option
list(option.tags_set.all()), # list of linked tags
))
# return this payload to browser
return HttpResponse(
# good practice to name your data parameters
json.dumps({ 'data': payload, }),
# always set content type -> good practice!
content_type='application/json'
)
# in the browser template, you can do something such as:
{% for option, tags in data %}
{{ option.something }}
{% for tag in tags %}
{{ tag.something }}
{% endfor %}
{% endfor %}

Related

Automatically get name of the column with for loop in django template

I would like to automatically retrieve the name of the column with a for loop.
If I know the name of the columns I can write the template in this way:
<ul>
{% for co in team %}
<li>{{ co.name }}</li>
<li>{{ co.team}}</li>
<li>{{ co.sport}}</li>
{% endfor %}
</ul>
But in my present case, I do not know the names of the columns of my table. How can I display them?
Thank you
You can obtain the fields of a model with:
fields = Model._meta.get_fields()
This is a tuple of Field [GitHub] objects. These Field objects have a name attribute, so we can obtain the field names with:
from operator import attrgetter
field_names = map(attrgetter('name'), Model._meta.get_fields())
In case we do not know what the model is, we can obtain a reference to the model of a model instance with type(..):
fields = type(some_instance)._meta.get_fields()
All this logic however does not really belong in the Django template, but in the view (by design, Django templates are a bit restrictive to prevent writing business logic in the template). We can thus pass a list of field names to the template:
def some_view(request):
teams = Team.objects.all()
field_names = [f.name for f in Team._meta.get_fields()]
return render(request, 'some_template.html',
{'teams': teams, 'field_names': field_names}
)
If you however want to print the values of these teams, then this will still not work, since we can not easily get an arbitrary attribute in a template. Then we can again shift some processing to the view:
from operator import attregetter
def some_view(request):
teams = Team.objects.all()
field_names = [f.name for f in Team._meta.get_fields()]
atgetters = [attrgetter(fn) for fn in field_names]
rows = [[ag(team) for ag in atgetters] for team in teams]
return render(request, 'some_template.html',
{'teams': teams, 'field_names': field_names, 'data': data}
)
So here data is a list containing lists, such that data[i][j] contains the value for a field with name field_name[j] for teams[i].

Why python dict is parsed in specific way in django template

I am creating a dict and return to django template
parsing = dict()
for idx,title in enumerate(titles):
link = parsing.setdefault(idx, {})
link.setdefault('title', title)
link.setdefault('image', image)
# print(link['image'])
return render(request, "blog/intro.html", {'parsing':parsing})
But can't understand why only this code works in template. Only with 1. Why?
{% for el in parsing.items %}
{{ el.1.title }}
{% endfor%}
EDIT:
let's say I need to put both title and image as elements of lists to dict. how can I also create list comprehension for images?
titles = [...]
images = [...]
parsing = [{'title': title, 'image': image} for title in titles]

Feeding JSON results from MongoDB into Django Template view doesn't display dictionary values

I'm returning a record set from MongoDB, parsing this as JSON and pushing this into a view and attempting to access dictionary values of each record in a template. I can print the records (as an individual record), but I cannot access the structure of each record as a dictionary. How can I get at the values?
def index(request):
results = settings.vali_collection.find({'index': [] })
json_docs = [json.dumps(doc, default=json_util.default) for doc in results]
return render(request, 'db.html', {'results': json_docs[0:3]})
In my template:
{% for result in results %}
{{ result.name}}
{{ result.items.name}}
{% endfor %}
My JSON looks like:
{"name": "Travel & Leisure", .., ..}
I can print the records in my template with {{record}}, but how do I get at the record as a dictionary? What I have above in the template doesn't work and returns nothing. But when I use:
{% for result in results %}
{{ result}}
{% endfor %}
I can get the records printed out to screen in JSON format. If I print out json_docs I get the following:
['{"name": "random", "sector": "random"}', {"name": "random", "sector": "random"}', {"name": "random", "sector": "random"}']
If the json is a dictionary itself, you need to have nested loops. Something like the following:
{%for i in gme%}
{%for l, k in i.items%}
<p> {{l}} {{k}} </p>
{%endfor%}
{%endfor%}
gme looks like this:
gme = [{"sdfje": 'sdfs',"sdfds": "sdf"},...]
The output is:
sdfje sdfs
sdfds sdf
Use json.loads(...) instead of json.dumps.
json.dumps(obj, skipkeys=False, ensure_ascii=True, check_circular=True, allow_nan=True, cls=None, indent=None, separators=None, encoding="utf-8", default=None, sort_keys=False, **kw)
Serialize obj to a JSON formatted str
json.loads(s[, encoding[, cls[, object_hook[, parse_float[, parse_int[, parse_constant[, object_pairs_hook[, **kw]]]]]]]])
Deserialize s (a str or unicode instance containing a JSON document) to a Python object using this conversion table.
What you are doing is serializing python dictionary to JSON formatted str, so you get string instead of dictionary.
You need to deserialize and for that, you need json.loads
If you can't use json.loads, as you said in comments section, then the results variable is not a json string. Maybe it is what you are looking for? Try to debug and see what is inside results
If you're using PyMongo 2.8 find returns a Cursor that can be sliced. This should work:
db.test.find()[20:25]
And this should also work:
result = db.test.find()
...
sliced_result = result[20:25]
You don't need to transform the result to JSON, you can transform the Cursor to a list and pass that directly to the template, like that:
def index(request):
results = settings.vali_collection.find({'index': []})[:3]
return render(request, 'db.html', {'results': list(results)})
You need to use list(results) to force the Cursor to execute the query. In your template you'll have a list of dicts, so this should work:
{% for result in results %}
{{ result.name }}
{{ result.items.name }}
{% endfor %}
result should be a dict. The only problem I see is that items is a template function in Django, this might confuse people that are reading your template, and would prevent you from doing something like this:
{% for result in results %}
{% for attr_name, value in result.items %}
{{ attr_name }}: {{ value }}
{% endfor %}
{% endfor %}
The inner for would show every attribute, and their value, of a document in your MongoDB collection.
If you're using an older version of PyMongo that doesn't allow slicing, you may need to do something like this:
def index(request):
results = settings.vali_collection.find({'index': []}).limit(3)
return render(request, 'db.html', {'results': list(results)})
limit limits the number of results returned by the Cursor.
I believe result in the template isn't what you think it is. Let's examine as follows:
In function index(request):
results = settings.vali_collection.find({'index': [] }) returns a list of dictionary-like objects.
json_docs = [json.dumps(doc, default=json_util.default) for doc in results] returns a list of JSON strings (not dictionaries).
So later on when you iterate through the sublist json_docs[0:3], you are just iterating through a list of strings which is why you cannot reference the .name and .items properties.
It looks like what you actually want is a dict() like object for each result in your template. To do this, avoid dumping the dictionaries into JSON.
So instead of:
# WRONG:
json_docs = [json.dumps(doc, default=json_util.default) for doc in results]
return render(request, 'db.html', {'results': json_docs[0:3]})
# /WRONG
...just pass the result in directly:
results = settings.vali_collection.find({'index': [] })
results = list(results) # This may or may not be necessary, depending on what mongo client you're using
return render(request, 'db.html', {'results' : results[:3])
Then in your template, when you iterate through results, each result should be a dictionary-like object which you can use result.name or result.items on.
BTW, the result.items.name reference in your code looks a bit weird to me (like it would return an error), but it's hard for me to debug without knowing what each record looks like.
Change your view like this
def index(request):
results = settings.vali_collection.find({'index': [] })
return render(request, 'db.html', json.dumps({"results":results[0:3]}))
You can now use your for loop to render

Django - Rating System View & Template

I have a content that I'd like to rate on multiple criteria.
Imagine this kind of model:
class Content(models.Model):
name = models.CharField(max_length=50)
class Criterion(models.Model):
name = models.CharField(max_length=50)
content = models.ForeignKey(Content)
class ContRate(models.Model):
user = models.ForeignKey(User, help_text="Who rated ?")
crit = models.ForeignKey(Criterion)
rate = models.DecimalField()
The user has a page displaying the content.
From this page, he can also rate the content on the criteria set
The rating will be done with Ajax.
Now I'm trying to implement the view & the template
view.py
#...
def viewcont(request, content_id):
"""The user can view a content & rate it"""
content = get_object_or_404(Content, pk=content_id)
RateFormSet = modelformset_factory(ContRate)
formset = RateFormSet(queryset=ContRate.objects.filter(content=content, user=request.user))
objs = {
'content': content,
'forms': formset,
}
return render_to_response('content/content_detail.html', objs
, context_instance=RequestContext(request)
)
#...
content_detail.html
<!-- ... -->
<div id="rating">
<ul>
{% for crit in content.crit_set.all %}
<li>
{{ crit }}
<div class="rateit"
data-rateit-value="the_actual_rating_if_already_there"
data-rateit-ispreset="true"
crit-id="{{ crit.id }}"></div>
</li>
{% endfor %}
</ul>
</div>
<!-- ... -->
Now how can I use the forms formset to display the actual rates ?
And how can I draw an empty form to be posted by Ajax from any clicked star ?
(I know the javascript/jQuery part)
Not sure what the point of the formset is here. The rates are all available via the criteria object, using the reverse foreign key to ContRate in exactly the same way as you've done from Criteria to Content.
To make this as efficient as possible, you probably want to get the relevant ratings in the view and bring them together into a single datastructure:
content = get_object_or_404(Content, pk=content_id)
criteria = content.criteria_set.all()
user_ratings = ContRate.objects.filter(content=content, user=request.user)
ratings_dict = dict((c.crit_id, c.rate) for c in user_ratings)
for crit in criteria:
crit.user_rating = ratings_dict.get(crit.id)
Now you can pass criteria directly to your template, and there you can iterate through it to show the user_rating for each one.
(Final point: "criteria" is plural, the singular is "criterion". :-)

How do I sort a column of Django elements inside a table?

How do I sort a column of Django elements inside a table?
I notice the Django Admin does this. How?
Any web resources available where I can read up on this?
*By "Django elements" I'm referring to the pluggable template objects that look like this:
{{ x.y }}
Worth having a look at the ChangeList view source code in the django admin:
http://code.djangoproject.com/svn/django/branches/releases/1.2.X/django/contrib/admin/views/main.py
The ordering process is roughly:
you click on a column header in the admin, this reloads the page with the relevant order parameter in the url, available in request.GET
this order parameter is parsed in the ChangeList view:
self.order_field, self.order_type = self.get_ordering()
queryset is generated:
self.query_set = self.get_query_set()
INparticular, the following lines in the function:
if self.order_field:
qs = qs.order_by('%s%s' % ((self.order_type == 'desc' and '-' or ''), self.order_field))
Using the django admin while understanding the source code is one of the best ways to learn django in my opinion!
If you're getting it from a query then use the order_by() method in the view. If it's a list then use the sort() method in the view.
I created this utility class after looking at how the admin did things back in Django 0.96.
It handles creation of order_by criteria based on GET parameters and provides context variables to be used to generating table header sort links which respect the current sort field and direction, reversing the direction when the same header is sorted by again.
ORDER_VAR = 'o'
ORDER_TYPE_VAR = 'ot'
class SortHeaders:
"""
Handles generation of an argument for the Django ORM's
``order_by`` method and generation of table headers which reflect
the currently selected sort, based on defined table headers with
matching sort criteria.
Based in part on the Django Admin application's ``ChangeList``
functionality.
"""
def __init__(self, request, headers, default_order_field=None,
default_order_type='asc', additional_params=None):
"""
request
The request currently being processed - the current sort
order field and type are determined based on GET
parameters.
headers
A list of two-tuples of header text and matching ordering
criteria for use with the Django ORM's ``order_by``
method. A criterion of ``None`` indicates that a header
is not sortable.
default_order_field
The index of the header definition to be used for default
ordering and when an invalid or non-sortable header is
specified in GET parameters. If not specified, the index
of the first sortable header will be used.
default_order_type
The default type of ordering used - must be one of
``'asc`` or ``'desc'``.
additional_params:
Query parameters which should always appear in sort links,
specified as a dictionary mapping parameter names to
values. For example, this might contain the current page
number if you're sorting a paginated list of items.
"""
if default_order_field is None:
for i, (header, query_lookup) in enumerate(headers):
if query_lookup is not None:
default_order_field = i
break
if default_order_field is None:
raise AttributeError('No default_order_field was specified and none of the header definitions given were sortable.')
if default_order_type not in ('asc', 'desc'):
raise AttributeError('If given, default_order_type must be one of \'asc\' or \'desc\'.')
if additional_params is None: additional_params = {}
self.header_defs = headers
self.additional_params = additional_params
self.order_field, self.order_type = default_order_field, default_order_type
# Determine order field and order type for the current request
params = dict(request.GET.items())
if ORDER_VAR in params:
try:
new_order_field = int(params[ORDER_VAR])
if headers[new_order_field][1] is not None:
self.order_field = new_order_field
except (IndexError, ValueError):
pass # Use the default
if ORDER_TYPE_VAR in params and params[ORDER_TYPE_VAR] in ('asc', 'desc'):
self.order_type = params[ORDER_TYPE_VAR]
def headers(self):
"""
Generates dicts containing header and sort link details for
all defined headers.
"""
for i, (header, order_criterion) in enumerate(self.header_defs):
th_classes = []
new_order_type = 'asc'
if i == self.order_field:
th_classes.append('sorted %sending' % self.order_type)
new_order_type = {'asc': 'desc', 'desc': 'asc'}[self.order_type]
yield {
'text': header,
'sortable': order_criterion is not None,
'url': self.get_query_string({ORDER_VAR: i, ORDER_TYPE_VAR: new_order_type}),
'class_attr': (th_classes and ' class="%s"' % ' '.join(th_classes) or ''),
}
def get_query_string(self, params):
"""
Creates a query string from the given dictionary of
parameters, including any additonal parameters which should
always be present.
"""
params.update(self.additional_params)
return '?%s' % '&'.join(['%s=%s' % (param, value) \
for param, value in params.items()])
def get_order_by(self):
"""
Creates an ordering criterion based on the current order
field and order type, for use with the Django ORM's
``order_by`` method.
"""
return '%s%s' % (
self.order_type == 'desc' and '-' or '',
self.header_defs[self.order_field][1],
)
Sample view:
from somewhere import SortHeaders
from django.contrib.auth.models import User
from django.shortcuts import render_to_response
LIST_HEADERS = (
('Username', 'username'),
('First Name', 'first_name'),
('Last Name', 'last_name'),
('Email', None),
)
def user_list(request):
sort_headers = SortHeaders(request, LIST_HEADERS)
users = User.objects.order_by(sort_headers.get_order_by())
return render_to_response('users/user_list.html', {
'users': users,
'headers': list(sort_headers.headers()),
})
Sample template (users/user_list.html):
{% load my_tags %}
<table cellspacing="0">
<thead>
<tr>
{% table_header headers %}
</tr>
</thead>
<tbody>
{% for user in users %}<tr class="{% cycle odd,even %}">
<td>{{ user.username }}</td>
<td>{{ user.first_name }}</td>
<td>{{ user.last_name }}</td>
<td>{{ user.email }}</td>
</tr>
{% endfor %}
</tbody>
</table>
Sample inclusion tag (templatetags/my_tags.py):
from django import template
def table_header(context, headers):
return {
'headers': headers,
}
register = template.Library()
register.inclusion_tag('table_header.html', takes_context=True)(table_header)
Sample inclusion tag template (table_header.html):
{% for header in headers %}<th{{ header.class_attr }}>
{% if header.sortable %}<a href="{{ header.url }}">{% endif %}
{{ header.text }}
{% if header.sortable %}</a>{% endif %}
</th>{% endfor %}

Categories

Resources