Django ORM Select Column From Two Model or More - django

I want select column with django orm from two model or more.
SELECT nim, k.kode, s.nama, sks, nilai, th_ajaran FROM mahasiswa_khs k, mata_kuliah_si s WHERE k.kode = s.kode AND nim = %s
This is my code
khs = Khs.objects.filter(nim=nim).values_list('kode').union(SI.objects.all().values_list('kode'))
But, went I call in template it's didn't show
It's my template :
{% for khs in khs %}
<tr>
<td>{{forloop.counter}}</td>
<td>{{khs.kode}}</td>
<td>{{khs.nama}}</td>
<td>{{khs.sks}}</td>
<td class="vertical-align-mid">{{khs.nilai}}</td>
<th>{{khs.th_ajaran}}</th>
</tr>
{% endfor %}
And the view :
khs = Khs.objects.filter(nim=nim).union(SI.objects.all().values_list('kode'))
context ={
'khs' : khs,
}
return render(request, 'mahasiswa/mahasiswa.html',context)

When you use .values_list or .values you're telling the ORM to only select those columns. As per the docs:
This is similar to values() except that instead of returning
dictionaries, it returns tuples when iterated over. Each tuple
contains the value from the respective field or expression passed into
the values_list() call — so the first item is the first field, etc.
For example:
from django.db.models.functions import Lower
Entry.objects.values_list('id', Lower('headline')) <QuerySet [(1, 'first entry'), ...]>```
Since you're using values_list, this means you're pulling out a dataset that looks like: [('kode_value_1',), ('kode_value_2', ), ...]. In your template you'd use:
{% for values in khs %}
{{ values.0 }}
{% endfor %}
In order to pull additional fields such as nama, you'd need to adjust your queryset:
khs = (
Khs.objects.filter(nim=nim)
.values_list('kode', 'nama')
.union(SI.objects.all().values_list('kode', 'nama'))
)
Then in your template you'd have:
{% for values in khs %}
kode: {{ values.0 }}
nama: {{ values.1}}
{% endfor %}
If you want to use the field names, then you should use values() instead of values_list() so that your template tag would be {{ values.kode }}
In the case that SI does not have the nama field, you can make an annotation that sets out a dummy value so the union can occur.
from django.db.models import Value, CharField
khs = (
Khs.objects.filter(nim=nim)
.values_list('kode', 'nama')
.union(
SI.objects
.annotate(nama=Value(None, output_field=CharField()))
.values_list('kode', 'nama')
)
)
I took a guess that nama is a CharField. You may need to change that as your data model requires. The annotation approach could also work to avoid the use of .values_list altogether if you'd like.

Related

Why won't Django template recognize list from object field?

I have an object field that stores a list. The Django template is treating the list like a string, it iterates over each character, not each list value.
Tried saving the object field in various ways, "['1','2']" and "1,2". Tried the "|make_list" filter.
models.py
class SpecialField(models.Model):
name = models.CharField(max_length=200,blank=True,null=True)
description = models.CharField(max_length=200,blank=True,null=True)
value_options = models.CharField(max_length=200,blank=True,null=True)
# value_options might be "['1','2']" or "red, green, blue"
views.py
object_field_list= SpecialField.objects.all()
context = {
'object_field_list': object_field_list,
}
return render(request, 'app1/template-detail.html', context)
template
{% for object_field in object_field_list%}
{% for list_value in object_field.value_options %}
<option>{{ list_value }}</option>
{% endfor %}
{% endfor %}
I was hoping for:
<option>1</option>
<option>2</option>
But I am getting:
<option>[</option>
<option>'</option>
<option>1</option>
<option>'</option>
<option>,</option>
<option>'</option>
<option>2</option>
<option>'</option>
<option>]</option>
You are wanting to show data from a model, so let's suppose you have a model
class SpecialField(models.Model):
name=models.CharField(max_length=101)
price = models.IntegerField(default=0)
Given the way you've set in in context you can show this in the template with
{% for obj in object_field_list %}
{{ obj.name}} - {{ obj.price }}
{% endfor %}
Obviously, you need to amend for your model
Tweak on your suggestion...it needs one more level of looping. Still can't get it to work.
models.py
class SpecialField(models.Model):
name=models.CharField(max_length=101)
price = models.IntegerField(default=0)
custom_list = models.CharField(max_length=200)
template
{% for obj in object_field_list %}
{{ obj.name}} - {{ obj.price }}
{% for list_value in obj.custom_list %}
{{ list_value }}
# this is where it's breaking for me
{% endfor %}
{% endfor %}
context = {
'object_field_list': SpecialField.objects.values_list('value_options', flat=True),
}
Should get you what you actually want to loop over.
EDIT: Sorry, I missed the fact you are storing a string rather than using an ArrayField or similar. The problem from your updated answer is the data you have isn't consistent. If it were simply all comma-separated values you could do:
object_field_list = [value_list.split(',') for value_list in SpecialField.objects.values_list('value_options', flat=True)]
but you will need some way of normalizing the data you're storing in value_options. How does the data get into the database? If it's via Django, you can apply some kind of cleaning method on the form or API endpoint that accepts the data.
Tried saving the object field in various ways, "['1','2']" and "1,2". Tried the "|make_list" filter.
If you have complete control over the incoming data, you would be better off normalizing the data: rather than storing a single value_options entry on SpecialField, you would remove that field and add a second model, e.g., SpecialFieldOption like
class SpecialFieldOption(models.Model):
name = models.CharField(max_length=200, blank=False)
field = models.ForeignKey(SpecialField, related_name='options')
# now you can do
SpecialField.objects.get(pk=1).options.all()
to loop over the list of all options for a given SpecialField.
This was the handiest solution...define a new list using split. https://stackoverflow.com/a/8318915/9268133. Thanks for everyone's help!

Django field value not showing in the template

I have the following query but when I loop over in my template it doesn't show the name field value.
views.py
hashtags = PhotoHashtag.objects.values('hashtag__name')\
.filter(hashtag__hashtagtype_id=3)\
.annotate(total_photos=Count('photo_id'))\
.order_by('-total_photos')
models.py
class PhotoHashtag(TimeStampedModel):
photo = models.ForeignKey('posts.Photo')
hashtag = models.ForeignKey('hashtags.Hashtag')
class Hashtag(TimeStampedModel):
hashtagtype = models.ForeignKey('hashtags.HashtagType')
name = models.CharField(max_length=250, unique=True)
template
{% for hashtag in hashtags %}
<li>({{ hashtag.name }}) ({{ hashtag.total_photos }})</li>
{% endfor %}
sql result
html
() (5)
() (2)
As you can see it shows the total_photos value but not the name value
You don't have a queryset of Hashtag objects. You have a ValuesQuerySet, created from the PhotoHashtag model, with two fields, hashtag__name and total_photos.
QuerySet.values returns a dictionary-like object ValuesQuerySet. This is why you can filter it.
The reason {{ hashtag.total_photos }} works is because 'total_photos' is a key in the resulting ValuesQuerySet object. Other keys in there are 'hashtag__name' and 'total_photos'. You can access them directly either by dot notation or like in a dict object if you're using something like Jinja2.
To make this work, just replace what you have with this:
{% for hashtag in hashtags %}
<li>({{ hashtag.hashtag__name }}) ({{ hashtag.total_photos }})</li>
{% endfor %}

Custom template filter syntax in django

So i have created a custom filter:
#register.filter(name='field_values')
def field_values(model, field):
return model.objects.values_list(field, flat=True)
that takes model class value and a field for an argument and returns a list of model objects for that particular field. Here is the template where I try to call it.
<tr>
{% for field in fields %}
<td>{{ model_class|field_values: field }} </td>
{% endfor %}
</tr>
model_class is a dynamically created model in the views as such
views.py
...
if form.is_valid(): # All validation rules pass
model_classes_field = form.cleaned_data['model_classes_field']
model_class = get_model('Directories', model_classes_field)
I was expecting everything to run smoothly but instead I get a "*field_values requires 1 arguments, 0 provided*" error. Why does this happen when i have already inserted an argument?? is it a problem with the filter syntax?
You have a space after the colon which is confusing the parser. Use it like this instead:
{{ model_class|field_values:field }}
(Note that your code is pretty inefficient: it's querying the database over and over again, once for each field. Rather than doing this, you should probably query it once in the view and group the output by field.)

Determine variable type within django template

I have a variable that I'm pulling into a table that sometimes is a date and sometimes is a string. If the variable is a date, I want to change the formatting:
<td>{{ action.extra_column|date:"M d" }}</td>
But if it is a string, I just want to display it as is:
<td>{{ action.extra_column }}</td>
If I try to format it and it is a string, I get no output for the variable.
How can I determine the type so that I can adjust my rendering based on type.
You could also implement a general template filter as an equivalent to the type() builtin:
# app/templatetags/util.py
from django import template
register = template.Library()
#register.filter
def get_type(value):
return type(value)
# template.html
{% load util %}
{% if extra_column|get_type == 'str' %}
String
{% elif extra_column|get_type == 'datetime.date' %}
Date
{% else %}
Oh no!
{% endif %}
I think Ignacio and Dirk are right, however. Can't you just have two keys (you say "array", but I assume you mean "dictionary" from the fact that the items have names) called date and detail?
# views.py
...
actions = [{
'some_property': 'some_value'
'date': None,
'detail': 'details'
},
{
'some_property': 'some_value'
'date': datetime.date.today(),
'detail': None
}]
...
# template.html
{% for action in actions %}
<td>{% if action.date %}{{ action.date|date:"M d" }}{% endif %}{{ action.detail }}</td>
{% endfor %}
# output
<td>details</td>
<td>Aug 19</td>
Like Ignacio Vazquez-Abrams pointed out in the first comment, that's not really a great way to code your logic. I would ensure that your variable has a certain type. That could be solved through an additional variable you add to the context or an object that holds the data and something that describes the type of data.
If you want to stick to your logic, a possible approach would be to write your own template filter (let's call it date_or_string). The filter could subclass the builtin date filter with the format parameter being optional. In case the parameter is passed it works like the normal date filter, without the parameter it simply returns the string. In a more complex scenario the filter could also do some type checking. Just an idea, i wouldn't actually put that kind of logic into the template.
I know I'm way behind on this (by three years) but I just got here looking to do something similar and came up with what I think is a decent solution.
Just add a function to your models like get_model_type and have it return something you'd expect from each model like so:
class MyModelOne(models.Model):
date_created = models.DateTimeField(auto_now_add=True)
first_name = models.CharField(max_length=255)
def get_model_type(self):
return "my_model_one"
class MyModelTwo(models.Model):
date_created = models.DateTimeField(auto_now_add=True)
other_field = models.CharField(max_length=255)
def get_model_type(self):
return "my_model_two"
Then in your template you can easily just call that function:
{% if model.get_model_type == 'my_model_one' %}
<p>Model One</p>
{% elif model.get_model_type == 'my_model_two' %}
<p>Model Two</p>
{% endif %}
Late to the party, but I just had this problem. The solution I went for is duck-typing, so:
{% if action.extra_column.year %}
{{ action.extra_column|date:"M y" }}
{% else %}
{{ action.extra_column }}
{% endif %}
Could you argue that this is definitely not the right way to do it? Probably. Will it get the job done without writing your own template filter and having even more code to maintain? Absolutely.
That's my approach:
#register.filter
def get_type(value):
""" It returns variable type as a pure string name """
return type(value).__name__
You can try this to recognize String vs List type:
{%if v_1.0.1|length == 0%}
<!--STR-->
{{v_1}}
{%else%}
<!--List-->
{{v_1.0}}
{%endif%}

Calling values from a dictionary (Django)

I am fairly new to Django and I am having trouble getting values to load into the HTML from the dictionary generated in the models.py that looks like this:
>>> generic_id = Generic.objects.get(pk=127)
>>> dict = generic_id._get_dict()
>>> dict
[{'field__name':'key1', 'value_char':'value1a', 'value_num':'value1'},{'field__name':'key2', 'value_char':'value2a', 'value_num':'value2'},{'field__name':'key3', 'value_char':'value3a', 'value_num':'value3'},{'field__name':'key4', 'value_char':'value4a', 'value_num':'value4'}]
>>> dict[2]['value_num']
Decimal('value2')
>>> dict[3]['value_char']
'value3a'
The HTML table looks like this:
<table>
<tr>
<td>Description1</td><td>{{value1}}</td>
<td>Description2</td><td>{{value2}}</td>
<td>Description3</td><td>{{value3a}}</td>
<td>Description4</td><td>{{value4}}</td>
</tr>
<tr>
<td>Name: {{ generic.name }}</td>
<td>E-mail: {{ generic.email }}</td>
<td>State: {{ generic.state }}
</table>
The code in the views.py right now looks like this:
def query_test(request, generic_id):
try:
a = Generic_table.objects.get(pk=generic_id)
except Generic_table.DoesNotExist:
raise Http404
t = loader.get_template('query_test.html')
c = RequestContext(request, {
'generic' : a, })
return HttpResponse(t.render(c))
Can someone give me some suggestions as to how to (and efficiently) get the appropriate values from the dictionary into the generated HTML?
Based on what your objects look like from your model, and your template, I would suggest trying this:
assuming:
a = Generic.objects.get(pk=generic_id)
# a == {'field__name':'key1', 'value_char':'value1a', 'value_num':'value1'}
views.py
from django.shortcuts import render_to_response, get_object_or_404
def query_test(request, generic_id):
a = get_object_or_404(Generic, pk=generic_id)
return render_to_response("query_test.html", a,
context_instance=RequestContext(request))
query_test.html
Name: {{field__name}}
Char: {{value_char}}
Num : {{value_num}}
Your view doesn't show that you are expecting more than one object, since you look for an id, so your template would end up formatting just one object.
Edit: In case you are trying to display a list of results
views.py might look something like this:
def query_test(request, generic_id=None):
if generic_id is not None:
a = Generic.objects.filter(pk=generic_id)
else:
a = Generic.objects.all()
c = {'generic': a}
# add some extra values
c['value1'] = "I am value1"
# add a whole dictionary of other values
c.update({'value2': "yay val2", 'value3a': 3, 'value4': "I am 4"})
return render_to_response("query_test.html", c,
context_instance=RequestContext(request))
And your template something like:
<table>
<tr>
<td>Description1</td><td>{{value1}}</td>
<td>Description2</td><td>{{value2}}</td>
<td>Description3</td><td>{{value3a}}</td>
<td>Description4</td><td>{{value4}}</td>
</tr>
{% for item in generic %}
<tr>
<td>Name: {{item.field__name}}</td>
<td>Char: {{item.value_char}}</td>
<td>Num : {{item.value_num}}</td>
</tr>
{% endfor %}
</table>
Edit2: Addressing the strange object you are sending to your template
Based on your updated question... That is not a dictionary. Its a list of dictionaries, and Its really strange the way you are pulling that data from the single model instance. But assuming that is what you really really want, you have a number of options..
1) Fix that data object BEFORE sending it to the template. I have no idea if you want all the elements in that list, or just a specific item.
not_a_generic_id = Generic.objects.get(pk=127)
not_a_dict = not_a_generic_id._get_dict()
dict_that_I_actually_want = not_a_dict[1]
return render_to_response("query_test.html", dict_that_I_actually_want)
2) Send that entire list to the template and loop over each item, and then access the values:
views.py
not_a_generic_id = Generic.objects.get(pk=127)
not_a_dict = not_a_generic_id._get_dict()
c = {"still_not_a_dict": not_a_dict}
return render_to_response("query_test.html", c)
template.html
<table>
{% for actual_dict in still_not_a_dict %}
<tr>
<td>Name: {{actual_dict.field__name}}</td>
<td>Char: {{actual_dict.value_char}}</td>
<td>Num : {{actual_dict.value_num}}</td>
</tr>
{% endfor %}
</table>
3) Even though the template does not let you actually access numeric indexes of lists because you are suppost to sort that data out yourself in the view...if you insist on accessing a specific index of that list in the template, do the same as #2 for the views.py, but:
template.html
{% for actual_dict in still_not_a_dict %}
{% if forloop.counter == 1 %}
{{actual_dict.value_char}}
{% endif %}
{% endfor %}