django models choices list - django

I am using django 1.7.2 and I have been given some code for a choices list to be placed in a model.
Here is the code:
YOB_TYPES = Choices(*(
((0, 'select_yob', _(' Select Year of Birth')),
(2000, 'to_present', _('2000 to Present'))) +
tuple((i, str(i)) for i in xrange(1990, 2000)) +
(1, 'unspecified', _('Prefer not to answer')))
)
....
year_of_birth_type = models.PositiveIntegerField(choices=YOB_TYPES, default=YOB_TYPES.select_yob, validators=[MinValueValidator(1)])
....
The above code gives the incorrect select list as shown below. I have read several SO posts & google searches and scoured the docs, but I am stuck and I am going around in circles.
This is how the current code displays the select list, which is wrong:
However, I want the select list to be displayed as follows:

You should wrap the last tuple in another tuple:
YOB_TYPES = Choices(*(
((0, 'select_yob', _(' Select Year of Birth')),
(2000, 'to_present', _('2000 to Present'))) +
tuple((i, str(i)) for i in xrange(1990, 2000)) +
((1, 'unspecified', _('Prefer not to answer')),))
)

1) You have to be careful when adding up tuples - you need to set a comma at the end so that python interprets this as a tuple:
YOB_TYPES = Choices(*(
((0, 'select_yob', _(' Select Year of Birth')),
(2000, 'to_present', _('2000 to Present'))) +
tuple((i, str(i)) for i in xrange(1990, 2000)) +
((1, 'unspecified', _('Prefer not to answer')),))
)
2) You have to order your choices according you want them to appear in the list - so the "2000 to present" should move one place to the back.
3) It would make more sense to use the empty_label attribute - and to remove the first item of your choices:
empty_label="(Select Year fo Birth)"

Related

Rewriting some functions for xlsxwriter box borders from Python 2 to Python 3

I am having some problem getting xlsxwriter to create box borders around a number of cells when creating a Excel sheet. After some searching I found a thread here where there was a example on how to do this in Python 2.
The link to the thread is:
python XlsxWriter set border around multiple cells
The answer I am trying to use is the one given by aubaub.
I am using Python 3 and is trying to get this to work but I am having some problems with it.
The first thing I did was changing xrange to range in the
def box(workbook, sheet_name, row_start, col_start, row_stop, col_stop),
and then I changed dict.iteritems() to dict.items() in
def add_to_format(existing_format, dict_of_properties, workbook):
Since there have been some changes to this from Python 2 to 3.
But the next part I am struggling with, and kinda have no idea what to do, and this is the
return(workbook.add_format(dict(new_dict.items() + dict_of_properties.items())))
part. I tried to change this by adding the two dictionaries in another way, by adding this before the return part.
dest = dict(list(new_dict.items()) + list(dict_of_properties.items()))
return(workbook.add_format(dest))
But this is not working, I have not been using dictionaries a lot before, and am kinda blank on how to get this working, and if it there have been some other changes to xlsxwriter or other factors that prevent this from working. Does anyone have some good ideas for how to solve this?
Here I have added a working example of the code and problem.
import pandas as pd
import xlsxwriter
import numpy as np
from xlsxwriter.utility import xl_range
#Adding the functions from aubaub copied from question on Stackoverflow
# https://stackoverflow.com/questions/21599809/python-xlsxwriter-set-border-around-multiple-cells/37907013#37907013
#And added the changes I thought would make it work.
def add_to_format(existing_format, dict_of_properties, workbook):
"""Give a format you want to extend and a dict of the properties you want to
extend it with, and you get them returned in a single format"""
new_dict={}
for key, value in existing_format.__dict__.items():
if (value != 0) and (value != {}) and (value != None):
new_dict[key]=value
del new_dict['escapes']
dest = dict(list(new_dict.items()) + list(dict_of_properties.items()))
return(workbook.add_format(dest))
def box(workbook, sheet_name, row_start, col_start, row_stop, col_stop):
"""Makes an RxC box. Use integers, not the 'A1' format"""
rows = row_stop - row_start + 1
cols = col_stop - col_start + 1
for x in range((rows) * (cols)): # Total number of cells in the rectangle
box_form = workbook.add_format() # The format resets each loop
row = row_start + (x // cols)
column = col_start + (x % cols)
if x < (cols): # If it's on the top row
box_form = add_to_format(box_form, {'top':1}, workbook)
if x >= ((rows * cols) - cols): # If it's on the bottom row
box_form = add_to_format(box_form, {'bottom':1}, workbook)
if x % cols == 0: # If it's on the left column
box_form = add_to_format(box_form, {'left':1}, workbook)
if x % cols == (cols - 1): # If it's on the right column
box_form = add_to_format(box_form, {'right':1}, workbook)
sheet_name.write(row, column, "", box_form)
#Adds dataframe with some data
frame1 = pd.DataFrame(np.random.randint(0,100,size=(10, 4)), columns=list('ABCD'))
writer = pd.ExcelWriter('test.xlsx', engine='xlsxwriter')
#Add frame to Excel sheet
frame1.to_excel(writer, sheet_name='Sheet1', startcol= 1, startrow= 2)
# Get the xlsxwriter workbook and worksheet objects.
workbook = writer.book
worksheet = writer.sheets['Sheet1']
#Add some formating to the table
format00 = workbook.add_format()
format00.set_bold()
format00.set_font_size(14)
format00.set_bg_color('#F2F2F2')
format00.set_align('center')
worksheet.conditional_format(xl_range(2, 1, 2, 5),
{'type': 'no_blanks',
'format': format00})
box(workbook, 'Sheet1', 3, 1, 12, 5)
writer.save()
I stumbled on this when trying to see if anyone else had posted a better way to deal with formats. Don't use my old way; whether you could make it work with Python 3 or not, it's pretty crappy. Instead, grab what I just put here: https://github.com/Yoyoyoyoyoyoyo/XlsxFormatter.
If you use sheet.cell_writer() instead of sheet.write(), then it will keep a memory of the formats you ask for on a cell-by-cell basis, so writing something new in a cell (or adding a border around it) won't delete the cell's old format, but adds to it instead.
A simple example of your code:
from format_classes import Book
book = Book(where_to_save)
sheet = book.add_book_sheet('Sheet1')
sheet.box(3, 1, 12, 5)
# add data to the box with sheet.cell_writer(...)
book.close()
Look at the code & the README to see how to do other things, like format the box's borders or backgrounds, write data, apply a format to an entire worksheet, etc.

django models choices list - decending order output

I asked this SO question yesterday.
The code I now have to display the choices list in my models.py is:
YOB_TYPES = Choices(*(
((0, 'select_yob', _(' Select Year of Birth')),
(2000, 'to_present', _('2000 to Present'))) +
tuple((i, str(i)) for i in xrange(1990, 2000)) +
((1, 'unspecified', _('Prefer not to answer')),))
)
....
year_of_birth_type = models.PositiveIntegerField(choices=YOB_TYPES, default=YOB_TYPES.select_yob, validators=[MinValueValidator(1)])
....
The choices list is now displayed with the year of birth running from 1990 to 1999 (ascending order) as shown below:
How do I change the code so that the year of birth dates are displayed 1999 to 1990 (decending order) as shown below:
I have searched but cannot locate anything related to my issue - reversing ( .reverse() ) the tuple output - maybe I am searching the wrong topic.
See this line:
tuple((i, str(i)) for i in xrange(1990, 2000)) +
Adjust like so:
tuple((i, str(i)) for i in xrange(1999, 1989, -1) +
The third argument specifies your "step", in this case -1 (to go in reverse). Remember that with xrange, the second parameter is not included in the iteration, so use 1989 rather than 1990 (the same reason why you used 2000 earlier, rather than 1999).

Get list of occurrences + count in a model Django?

Imagine I have the following model:
class Person(models.Model):
...other stuff...
optional_first_name= models.CharField(max_length=50, blank=True)
How would I go about writing a request that returns an array of the most popular names, in decreasing order of occurence, with their counts, while ignoring the empty names?
i.e. for a database with 13 Leslies, 8 Andys, 3 Aprils, 1 Ron and 18 people who haven't specified their name, the output would be:
[('leslie', 13), ('andy', 8), ('april', 3), ('ron', 1)]
The closest I can get is by doing the following:
q= Person.objects.all()
q.query.group_by=['optional_first_name']
q.query.add_count_column()
q.values_list('optional_first_name', flat= True)
But it's still not quite what I want.
After some digging, finally found out:
Person.objects.values('optional_first_name').annotate(c=Count('optional_first_name')).order_by('-c')

How to get Date between two Dates Django

I need to check is there any object exist for given time Interval? How can I do that?How can I translate this Mysql into Django:
SELECT *
FROM `event_event`
WHERE (startDate BETWEEN "2010-10-1" AND "2010-10-5")
OR (endDate BETWEEN "2010-10-1" AND "2010-10-5")
I am currently using
Event.objects.filter(Q(startDate__range(datetime(2010,10,1),datetime(2010,10,5)))|Q(endDate__range(datetime(2010,10,1),datetime(2010,10,5))))
But I am not getting any object when I am using Django filter.Please suggest me where I am wrong.
Do
print Event.objects.filter(Q(startDate__range(datetime(2010,10,1),datetime(2010,10,5)))|Q(endDate__range(datetime(2010,10,1),datetime(2010,10,5)))).query
And see what SQL it produces, it'll help you spot the differences.
Try To Do This:
Event.objects.filter(Q(startDate >= datetime(2010, 10, 1), startDate <= datetime(2010, 10, 5)) | Q(endDate >= datetime(2011, 10, 1), endDate <= datetime(2010, 10, 5)))

Using the "extra fields " from django many-to-many relationships with extra fields

Django documents give this example of associating extra data with a M2M relationship. Although that is straight forward, now that I am trying to make use of the extra data in my views it is feeling very clumsy (which typically means "I'm doing it wrong").
For example, using the models defined in the linked document above I can do the following:
# Some people
ringo = Person.objects.create(name="Ringo Starr")
paul = Person.objects.create(name="Paul McCartney")
me = Person.objects.create(name="Me the rock Star")
# Some bands
beatles = Group.objects.create(name="The Beatles")
my_band = Group.objects.create(name="My Imaginary band")
# The Beatles form
m1 = Membership.objects.create(person=ringo, group=beatles,
date_joined=date(1962, 8, 16),
invite_reason= "Needed a new drummer.")
m2 = Membership.objects.create(person=paul, group=beatles,
date_joined=date(1960, 8, 1),
invite_reason= "Wanted to form a band.")
# My Imaginary band forms
m3 = Membership.objects.create(person=me, group=my_band,
date_joined=date(1980, 10, 5),
invite_reason= "Want to be a star.")
m4 = Membership.objects.create(person=paul, group=my_band,
date_joined=date(1980, 10, 5),
invite_reason= "Wanted to form a better band.")
Now if I want to print a simple table that for each person gives the date that they joined each band, at the moment I am doing this:
bands = Group.objects.all().order_by('name')
for person in Person.objects.all():
print person.name,
for band in bands:
print band.name,
try:
m = person.membership_set.get(group=band.pk)
print m.date_joined,
except:
print 'NA',
print ""
Which feels very ugly, especially the "m = person.membership_set.get(group=band.pk)" bit. Am I going about this whole thing wrong?
Now say I wanted to order the people by the date that they joined a particular band (say the beatles) is there any order_by clause I can put on Person.objects.all() that would let me do that?
Any advice would be greatly appreciated.
You should query the Membership model instead:
members = Membership.objects.select_related('person', 'group').all().order_by('date_joined')
for m in members:
print m.band.name, m.person.name, m.date_joined
Using select_related here we avoid the 1 + n queries problem, as it tells the ORM to do the join and selects everything in one single query.