Let's say I have this Person class that consist of last_name string field.
I would like to display links of all first letters of names existing in db.
So for example: ABD E...
when there's Adams, Brown, Douglas, Evans and no one that last_name starts with C.
Of course view is not a problem here as I want to prepare all of this on backend. So the question is how to write good model's or view's function that will provide this.
I would like it to be DB-independent, however tricks for any particular DB would be a bonus.
Also, I would like this to be quite well optimized in terms of speed because it could be a lot of names. It should be less naive than this most simple algorithm:
So for example (in views.py):
names = Person.objects.values_list('last_name', flat=True)
letters = {name[0] for name in names}
letters_sorted = sorted(letters)
Of course I could order_by first or assign new attribute to each objects (containing first letter) but I don't think it will speed up the process.
I also think that assuming that all letters are in use is bad assumption if I would go check each letter if at least one name for this letter exists;-)
Which approach would be best effective for databases and django here?
You could also use the orm methods to generate the list of initals (including count):
from django.db.models import Count
from django.db.models.functions import Left
initials = (Person
.objects
.annotate(initial=Left('last_name', 1))
.values('initial')
.annotate(count=Count('initial'))
.order_by('initial'))
This will result in something like
<QuerySet [{'initial': 'A', 'count': 1},
{'initial': 'B', 'count': 2},
...
{'initial': 'Y', 'count': 1}]>
I would sort the names first, then unpack the names as arguments for zip
, then consume just the first tuple that zip
yields:
names = sorted(Person.objects.values_list('last_name', flat=True))
first_letters = next(zip(*names))
This doesn't use sets or remove duplicates or anything like that. Is that critical? If it is, you could do this:
names = Person.objects.values_list('last_name', flat=True)
first_letters = sorted(set(next(zip(*names))))
Though this would be hardly more performant that what you've already written.
import string
alphabet_list = list(string.ascii_lowercase) + list(string.ascii_uppercase)
result = dict(map(lambda x: (x, Person.objects.filter(name__start_with=x).exists()), alphabet_list))
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.