I'm still pretty new to using Python... and I'm trying to work out how to apply Data Validation to a region of an Excel worksheet using a named range of cells... amongst other things.
For the code below, I'm looking to find out how to:-
forumla1
item in DataValidation()
...as well as finding better ways to:-
DefinedName()
attr_text
and when applying a validation object to a (named) rangeI've done much with Google search and Stack Overflow... but without much success. I've also been through the relevant parts of https://openpyxl.readthedocs.io/en/stable/ and whilst the examples have been helpful, the actual definitions, etc of the objects, their usage, etc have been less so.
I'm using (Anaconda) Python v3.6.5 under Windows 10 64-bit, openpyxl 3.0.3.
I'd appreciate any pointers or suggestions...
The code:
import openpyxl
from openpyxl import Workbook
from openpyxl.worksheet.datavalidation import DataValidation
# ---- Create the 'Data' worksheet - to set-up defined ranges
data_headers = [ "StatusValues", "ActivityValues" ]
status_values = [ "ROOKIE", "ACTIVE", "SUSPENDED", "RETIRED" ]
activity_values = [ "ASSIGNED", "TRAINING", "TRAVELLING" ]
# ----
wb = Workbook()
ws_name = wb.create_sheet('Data') # Does NOT make it active (current)
ws = wb.get_sheet_by_name('Data')
ws.append(data_headers) # Create header line
for idx, row in enumerate(status_values): # Populate the worksheet with 'Status' items...
ws.cell(column=1, row=idx+2,
value=status_values[idx])
for idx, row in enumerate(activity_values): # ...and 'Activity' items
ws.cell(column=2, row=idx+2,
value=activity_values[idx])
# ---- Set-up the named ranges for the entire workbook
# There HAS to be a BETTER way of specifying the 'worksheet:range' thing(!)
lo = 2
hi = len(status_values) + 1
sbuf = 'Data!$A$%s:$A$%s'%(lo, hi)
sv_range = openpyxl.workbook.defined_name.DefinedName('StatusValues', attr_text=sbuf)
wb.defined_names.append(sv_range)
lo = 2
hi = len(activity_values) + 1
sbuf = 'Data!$B$%s:$B$%s'%(lo, hi)
av_range = openpyxl.workbook.defined_name.DefinedName('ActivityValues', attr_text=sbuf)
wb.defined_names.append(av_range)
# ---- Get some data into the 'main' worksheet
ws = wb.worksheets[0] # Use the 'original' worksheet
ws.title = "Operatives"
ws.append(["Beast", "Status", "Activity"])
ws["A2"] = "Dog"
ws["A3"] = "Cat"
ws["A4"] = "Wombat"
ws["A5"] = "Serval"
ws["A6"] = "Camel"
# Create a data-validation object with list validation
dv = DataValidation(type="list",
formula1='"ROOKIE,ACTIVE,SUSPENDED,RETIRED"', # How can I use 'status_values'...
allow_blank=True)
dv2 = DataValidation(type="list",
formula1='"ASSIGNED,TRAINING,TRAVELLING"', # ...and 'activity_values' lists?
allow_blank=True)
# Add the data validation objects to the worksheet
ws.add_data_validation(dv)
ws.add_data_validation(dv2)
# ...and to specific range (ugh... so ugly.. again, has to be a better way..)
dv_app = "B2:B" + str(ws.max_row)
dv.add(dv_app)
dv_app = "C2:C" + str(ws.max_row)
dv2.add(dv_app)
# ----
wb.save(filename = 'validation.xlsx')
exit(0)
I have the same problem. You code, Skeeve, was very helpful and I believe that, after 9 months you have found a solution to use the list name as argument to formula1 parameter. Thanks
Actually, I found a way:
str1 = ','.join(status_values)
str1 = '"'+str1+'"'
dv = DataValidation(type="list", formula1=str1, allow_blank=True)
There is also an alternative way of doing this. You can provide a range address eg =Data:$A$2.$A$5 as the argument to the formula1 parameter (and remember that this does not need double quotes around it).
Linking to a range prevents bloating of your spreadsheet in case there is a large number of items in that list and you are doing data validation on a lot of cells. It will also take care of a scenario where a user updates one of the values on the Data
tab of the spreadsheet
To make this work, I updated the code that Skeeve had posted as follows:
sbuf
variable with subf1
and sbuf2
as we have two different data ranges (makes it a little easier to follow)=
before sbuf1
and sbuf2
in the argument for formula1
ws = wb.get_sheet_by_name('Data')
is now deprecated so replaced that with ws = wb['Data']
import openpyxl
from openpyxl import Workbook
from openpyxl.worksheet.datavalidation import DataValidation
# ---- Create the 'Data' worksheet - to set-up defined ranges
data_headers = [ "StatusValues", "ActivityValues" ]
status_values = [ "ROOKIE", "ACTIVE", "SUSPENDED", "RETIRED" ]
activity_values = [ "ASSIGNED", "TRAINING", "TRAVELLING" ]
# ----
wb = Workbook()
ws_name = wb.create_sheet('Data') # Does NOT make it active (current)
ws = wb['Data']
ws.append(data_headers) # Create header line
for idx, row in enumerate(status_values): # Populate the worksheet with 'Status' items...
ws.cell(column=1, row=idx+2,
value=status_values[idx])
for idx, row in enumerate(activity_values): # ...and 'Activity' items
ws.cell(column=2, row=idx+2,
value=activity_values[idx])
# ---- Set-up the named ranges for the entire workbook
# There HAS to be a BETTER way of specifying the 'worksheet:range' thing(!)
lo = 2
hi = len(status_values) + 1
sbuf1 = 'Data!$A$%s:$A$%s'%(lo, hi)
sv_range = openpyxl.workbook.defined_name.DefinedName('StatusValues', attr_text=sbuf1)
wb.defined_names.append(sv_range)
lo = 2
hi = len(activity_values) + 1
sbuf2 = 'Data!$B$%s:$B$%s'%(lo, hi)
av_range = openpyxl.workbook.defined_name.DefinedName('ActivityValues', attr_text=sbuf2)
wb.defined_names.append(av_range)
# ---- Get some data into the 'main' worksheet
ws = wb.worksheets[0] # Use the 'original' worksheet
ws.title = "Operatives"
ws.append(["Beast", "Status", "Activity"])
ws["A2"] = "Dog"
ws["A3"] = "Cat"
ws["A4"] = "Wombat"
ws["A5"] = "Serval"
ws["A6"] = "Camel"
# Create a data-validation object with list validation
dv = DataValidation(type="list",
formula1='=' + sbuf1,
allow_blank=True)
dv2 = DataValidation(type="list",
formula1='=' + sbuf2,
allow_blank=True)
# Add the data validation objects to the worksheet
ws.add_data_validation(dv)
ws.add_data_validation(dv2)
# ...and to specific range (ugh... so ugly.. again, has to be a better way..)
dv_app = "B2:B" + str(ws.max_row)
dv.add(dv_app)
dv_app = "C2:C" + str(ws.max_row)
dv2.add(dv_app)
# ----
wb.save(filename = 'validation.xlsx')
exit(0)
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.