I had a huge registration table with 112 fields. For a particular search I want to compare 17 fields & assign colors to variable say 'clrSelected'.
My code is :
reg = Regisration.objects.filter('some condition').order_by("name")
for r in reg:
if r.name=='abc': clrSelected='#fff'
if r.type=='1': clrSelected='#000'
if r.appl=='10': clrSelected='#c1ff51'
if r.code=='': clrSelected='#60c5f7'
if r.qlty=='first': clrSelected='#f99334'
...
...
there will be only one if condition, which need to be colored. Which means the field(from the dictionary) to be compared will change based on the user selection.
I want to access the field name from a dictionary like this
flds = {'1':'name', '2':'type', '3':'appl', '4':'code', '5':'qlty',...}
And use it something like this
if r.flds['1']=='abc': clrSelected='#fff'
How could i use the fields as above. I am using django 1.4 & python 2.7
Just to answer the question, you could use getattr:
if getattr(r, flds['1']) == 'abc': clrSelected = '#fff'
However, I am pretty sure that you could go with a different kind of implementation in this case which doesn't require using a dict like this.
I would suggest using a three tuple list: (fieldName, value, color)
some_list = [('name', 'abc', '#fff'), ('type', '1', '#000'), ...]
And, then use this list to determine the color:
for fieldName, value, color in some_list:
if getattr(r, fieldName) == value:
clrSelected = color
Looking at your implementation, it seems the color will be based on the last if condition which matches. If that is the case, you could create some_list in reverse order and break on the first matching condition.
I have a dictionary that looks like:
dictionary = {'article1.txt': {'harry': 3, 'hermione': 2, 'ron': 1},
'article2.txt': {'dumbledore': 1, 'hermione': 3},
'article3.txt': {'harry': 5}}
And I'm interested in picking the article with the most number of occurences of Hermione. I already have code that selects the outer keys (article1.txt, article2.txt) and inner key hermione.
Now I want to be able to have code that sorts the dictionary into a list of ascending order for the highest number occurrences of the word hermione. In this case, I want a list such that ['article1.txt', 'article2.txt']. I tried it with the following code:
#these keys are generated from another part of the program
keys1 = ['article1.txt', 'article2.txt']
keys2 = ['hermione', 'hermione']
place = 0
for i in range(len(keys1)-1):
for j in range(len(keys2)-1):
if articles[keys1[i]][keys2[j]] > articles[keys1[i+1]][keys2[j+1]]:
ordered_articles.append(keys1[i])
place += 1
else:
ordered_articles.append(place, keys1[i])
But obviously (I'm realizing now) it doesn't make sense to iterate through the keys to check if dictionary[key] > dictionary[next_key]. This is because we would never be able to compare things not in sequence, like dictionary[key[1]] > dictionary[key[3]].
Help would be much appreciated!
It seems that what you're trying to do is sort the articles by the amount of 'hermiones' in them. And, python has a built-in function that does exactly that (you can check it here). You can use it to sort the dictionary keys by the amount of hermiones each of them points to.
Here's a code you can use as example:
# filters out articles without hermione from the dictionary
# value here is the inner dict (for example: {'harry': 5})
dictionary = {key: value for key, value in dictionary.items() if 'hermione' in value}
# this function just returns the amount of hermiones in an article
# it will be used for sorting
def hermione_count(key):
return dictionary[key]['hermione']
# dictionary.keys() is a list of the keys of the dictionary (the articles)
# key=... here means we use hermione_count as the function to sort the list
article_list = sorted(dictionary.keys(), key=hermione_count)
If I have an ordered sequential list in my django template:
my_list = [
(1, "Billy Holiday"),
(2, "Louis Armstrong"),
# Number 3 is missing!
(4, "Ella Fitzgerald"),
(5, "Frank Sinatra"),
]
And I want to show something like this in my html:
1. Billy Holiday
2. Louis Armstrong
-- Some rows may be missing --
4. Ella Fitzgerald
5. Frank Sinatra
Is there a clever way to do this? I'm trying to accomplish this using Django templates. The idea would be to look at the previous iteration of the loop, and identify if rows are missing based on the counter values.
It seems like you try to move some logic from views to templates. I would strongly advise against this approach. It is not testable, harder to maintain, most likely will conflict with DRY principle.
So just doing the work in the view would probably be better.
However, if you are totally adamant that you want this done in template, you can make your own template filter:
someapp/templatetags/app_tags.py
from django import template
register = template.Library()
#register.filter
def set_missing_items(collection):
new_list = []
last = 0
for item in collection:
if item[0] != last + 1:
new_list.append('-- Some rows may be missing --')
new_list.append(item)
last = item[0]
return new_list
And then use it as follows:
template.html
{% load app_tags %}
{% for item in list|set_missing_items %}
{{ item }}
{% endif %}
Make sure to follow all steps from the documentation to make your custom filters work (like having proper folder structure with __init__.py files, making sure that app that has those filters is installed, etc).
Maybe a simple approach will be build the list this way:
my_list = [
(1, "Billy Holiday"),
(2, "Louis Armstrong"),
(3, ""),
(4, "Ella Fitzgerald"),
(5, "Frank Sinatra"),
]
and write a if for take care of the blank data inside the loop.
For example, if I have an array of datetime.date objects, I would like to apply a date format filter to each of its elements, while still making use of the default string representation of the array.
Given a date array that looks like:
[datetime.date(2011, 2, 28), datetime.date(2011, 3, 1), datetime.date(2011, 3, 2)]
Assuming that I already passed it to the template's context, I'd like to do this in the template:
<script>
// ...
var dates = {{ my_date_array|date:'b d, Y' }};
// ...
</script>
so it produces:
var dates = ['Feb 28, 2011', 'Mar 1, 2011', 'Mar 2, 2011'];
..instead of having to loop through the elements of the array.
Is this possible by default, without creating a custom filter?
Looking at the source, I'd say that's not possible using the default date filter.
You will have to either use a loop in your template, or create a custom filter that accepts a list of date objects.
Update:
It should be relatively easy to create your own filter by making use of the existing one. For example:
from django.template.defaultfilters import date
from django import template
register = template.Library()
# Only mildly tested. Use with caution.
def datelist(values, arg=None):
try:
outstr = "', '".join([date(v, arg) for v in values])
except TypeError: # non-iterable?
outstr = date(values, arg)
return "['%s']" % outstr
register.filter('datelist', datelist)
If you don't like that approach for determining iterable objects, you could also use:
# requires Python >=2.4
from collections import Iterable
if isinstance(values, Iterable):
# ....
I'm in a situation where I must output a quite large list of objects by a CharField used to store street addresses.
My problem is, that obviously the data is ordered by ASCII codes since it's a Charfield, with the predictable results .. it sort the numbers like this;
1, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 2, 20, 21....
Now the obvious step would be to change the Charfield the proper field type (IntegerField let's say), however it cannot work since some address might have apartments .. like "128A".
I really don't know how I can order this properly ..
If you're sure there are only integers in the field, you could get the database to cast it as an integer via the extra method, and order by that:
MyModel.objects.extra(
select={'myinteger': 'CAST(mycharfield AS INTEGER)'}
).order_by('myinteger')
Django is trying to deprecate the extra() method, but has introduced Cast() in v1.10. In sqlite (at least), CAST can take a value such as 10a and will cast it to the integer 10, so you can do:
from django.db.models import IntegerField
from django.db.models.functions import Cast
MyModel.objects.annotate(
my_integer_field=Cast('my_char_field', IntegerField())
).order_by('my_integer_field', 'my_char_field')
which will return objects sorted by the street number first numerically, then alphabetically, e.g. ...14, 15a, 15b, 16, 16a, 17...
If you're using PostgreSQL (not sure about MySQL) you can safely use following code on char/text fields and avoid cast errors:
MyModel.objects.extra(
select={'myinteger': "CAST(substring(charfield FROM '^[0-9]+') AS INTEGER)"}
).order_by('myinteger')
Great tip! It works for me! :) That's my code:
revisioned_objects = revisioned_objects.extra(select={'casted_object_id': 'CAST(object_id AS INTEGER)'}).extra(order_by = ['casted_object_id'])
I know that I’m late on this, but since it’s strongly related to the question, and that I had a hard time finding this:
You have to know that you can directly put the Cast in the ordering option of your model.
from django.db import models
from django.db.models.functions import Cast
class Address(models.Model):
street_number = models.CharField()
class Meta:
ordering = [
Cast("street_number", output_field=models.IntegerField()),
]
From the doc about ordering:
You can also use query expressions.
And from the doc about database functions:
Functions are also expressions, so they can be used and combined with other expressions like aggregate functions.
The problem you're up against is quite similar to how filenames get ordered when sorting by filename. There, you want "2 Foo.mp3" to appear before "12 Foo.mp3".
A common approach is to "normalize" numbers to expanding to a fixed number of digits, and then sorting based on the normalized form. That is, for purposes of sorting, "2 Foo.mp3" might expand to "0000000002 Foo.mp3".
Django won't help you here directly. You can either add a field to store the "normalized" address, and have the database order_by that, or you can do a custom sort in your view (or in a helper that your view uses) on address records before handing the list of records to a template.
In my case i have a CharField with a name field, which has mixed (int+string) values, for example. "a1", "f65", "P", "55" e.t.c ..
Solved the issue by using the sql cast (tested with postgres & mysql),
first, I try to sort by the casted integer value, and then by the original value of the name field.
parking_slots = ParkingSlot.objects.all().extra(
select={'num_from_name': 'CAST(name AS INTEGER)'}
).order_by('num_from_name', 'name')
This way, in any case, the correct sorting works for me.
In case you need to sort version numbers consisting of multiple numbers separated by a dot (e.g. 1.9.0, 1.10.0), here is a postgres-only solution:
class VersionRecordManager(models.Manager):
def get_queryset(self):
return super().get_queryset().extra(
select={
'natural_version': "string_to_array(version, '.')::int[]",
},
)
def available_versions(self):
return self.filter(available=True).order_by('-natural_version')
def last_stable(self):
return self.available_versions().filter(stable=True).first()
class VersionRecord(models.Model):
objects = VersionRecordManager()
version = models.CharField(max_length=64, db_index=True)
available = models.BooleanField(default=False, db_index=True)
stable = models.BooleanField(default=False, db_index=True)
In case you want to allow non-numeric characters (e.g. 0.9.0 beta, 2.0.0 stable):
def get_queryset(self):
return super().get_queryset().extra(
select={
'natural_version':
"string_to_array( "
" regexp_replace( " # Remove everything except digits
" version, '[^\d\.]+', '', 'g' " # and dots, then split string into
" ), '.' " # an array of integers.
")::int[] "
}
)
I was looking for a way to sort the numeric chars in a CharField and my search led me here. The name fields in my objects are CC Licenses, e.g., 'CC BY-NC 4.0'.
Since extra() is going to be deprecated, I was able to do it this way:
MyObject.objects.all()
.annotate(sorting_int=Cast(Func(F('name'), Value('\D'), Value(''), Value('g'), function='regexp_replace'), IntegerField()))
.order_by('-sorting_int')
Thus, MyObject with name='CC BY-NC 4.0' now has sorting_int=40.
All the answeres in this thread did not work for me because they are assuming numerical text. I found a solution that will work for a subset of cases. Consider this model
Class Block(models.Model):
title = models.CharField()
say I have fields that sometimes have leading characters and trailing numerical characters If i try and order normally
>>> Block.objects.all().order_by('title')
<QuerySet [<Block: 1>, <Block: 10>, <Block: 15>, <Block: 2>, <Block: N1>, <Block: N12>, <Block: N4>]>
As expected, it's correct alphabetically, but makes no sense for us humans. The trick that I did for this particular use case is to replace any text i find with the number 9999 and then cast the value to an integer and order by it.
for most cases that have leading characters this will get the desired result. see below
from django.db.models.expressions import RawSQL
>>> Block.objects.all()\
.annotate(my_faux_integer=RawSQL("CAST(regexp_replace(title, '[A-Z]+', '9999', 'g') AS INTEGER)", ''))\
.order_by('my_faux_integer', 'title')
<QuerySet [<Block: 1>, <Block: 2>, <Block: 10>, <Block: 15>, <Block: N1>, <Block: N4>, <Block: N12>]>