Skip to content

Commit 3a7b62d

Browse files
committed
rolling out new feature: make response from a query set using pyexcel v0.1.5
1 parent 2e1e9db commit 3a7b62d

File tree

8 files changed

+117
-54
lines changed

8 files changed

+117
-54
lines changed

db.sqlite3

0 Bytes
Binary file not shown.

django_excel/__init__.py

Lines changed: 18 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,15 @@
1+
"""
2+
django_excel
3+
~~~~~~~~~~~~~~~~~~~
4+
5+
A django middleware that provides one application programming interface
6+
to read and write data in different excel file formats
7+
8+
:copyright: (c) 2015 by Onni Software Ltd.
9+
:license: New BSD License
10+
"""
11+
12+
113
from django.core.files.uploadhandler import MemoryFileUploadHandler, TemporaryFileUploadHandler
214
from django.core.files.uploadedfile import InMemoryUploadedFile, TemporaryUploadedFile
315
from django.http import HttpResponse
@@ -11,10 +23,10 @@ def _get_file_extension(self):
1123
return extension
1224

1325
def load_single_sheet(self, sheet_name=None, **keywords):
14-
return pe.load_from_memory(self._get_file_extension(), self.file.read(), sheet_name, **keywords)
26+
return pe.get_sheet(file_type=self._get_file_extension(), content=self.file.read(), sheet_name=sheet_name, **keywords)
1527

16-
def load_book(self):
17-
return pe.load_book_from_memory(self._get_file_extension(), self.file.read())
28+
def load_book(self, **keywords):
29+
return pe.get_book(file_type=self._get_file_extension(), content=self.file.read(), **keywords)
1830

1931
def save_to_database(self, model=None,
2032
sheet_name=None, name_columns_by_row=0, name_rows_by_column=-1, **keywords):
@@ -23,12 +35,12 @@ def save_to_database(self, model=None,
2335
name_rows_by_column=name_rows_by_column,
2436
**keywords)
2537
if sheet:
26-
sheet.save_to_django_model(model)
38+
sheet.save_to_django_model(model, **keywords)
2739

2840
def save_book_to_database(self, models=None, **keywords):
2941
book = self.load_book(**keywords)
3042
if book:
31-
book.save_to_django_models(models)
43+
book.save_to_django_models(models, **keywords)
3244

3345

3446
class ExcelInMemoryUploadedFile(ExcelMixin, InMemoryUploadedFile):
@@ -72,6 +84,7 @@ def new_file(self, file_name, *args, **kwargs):
7284
make_response_from_dict,
7385
make_response_from_records,
7486
make_response_from_book_dict,
87+
make_response_from_query_sets
7588
)
7689

7790
def make_response_from_a_table(model, file_type, status=200, **keywords):

doc/source/conf.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -49,16 +49,16 @@
4949

5050
# General information about the project.
5151
project = u'django-excel'
52-
copyright = u'2015, C. W.'
52+
copyright = u'2015, Onni Software Ltd.'
5353

5454
# The version info for the project you're documenting, acts as replacement for
5555
# |version| and |release|, also used in various other places throughout the
5656
# built documents.
5757
#
5858
# The short X.Y version.
59-
version = '0.0.1'
59+
version = '0.0.2'
6060
# The full version, including alpha/beta/rc tags.
61-
release = '0.0.1'
61+
release = '0.0.2'
6262

6363
# The language for content autogenerated by Sphinx. Refer to documentation
6464
# for a list of supported languages.

doc/source/custom-export.png

5.85 KB
Loading

doc/source/index.rst

Lines changed: 52 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -99,7 +99,7 @@ Then run the test application::
9999
Handle excel file upload and download
100100
++++++++++++++++++++++++++++++++++++++
101101

102-
This example shows how to process uploaded excel file and how to make data download as an excel file. Open your browser and visit http://localhost:8000/upload, you shall see this upload form:
102+
This example shows how to process uploaded excel file and how to make data download as an excel file. Open your browser and visit http://localhost:8000/polls/upload, you shall see this upload form:
103103

104104
.. image :: upload-form.png
105105
@@ -125,7 +125,7 @@ Please open the file `polls/views.py <https://github.com/chfw/django-excel/blob/
125125

126126
**UploadFileForm** is html widget for file upload form in the html page. Then look down at **filehandle**. It is an instance of either ExcelInMemoryUploadedFile or TemporaryUploadedExcelFile, which inherit ExcelMixin and hence have a list of conversion methods to call, such as get_sheet, get_array, etc.
127127

128-
For the response, :meth:`~django_excel.make_response` converts :class:`~pyexcel.Sheet` instance obtained via :meth:`~django_excel.ExcelMixin.get_sheet` into a csv file for download.
128+
For the response, :meth:`~django_excel.make_response` converts :class:`pyexcel.Sheet` instance obtained via :meth:`~django_excel.ExcelMixin.get_sheet` into a csv file for download.
129129

130130
Please feel free to change those functions according to :ref:`the mapping table <data-types-and-its-conversion-funcs>`.
131131

@@ -173,7 +173,7 @@ into the following data models::
173173
.. note::
174174
Except the added "slug" field, **Question** and **Choice** are copied from Django tutoial part 1.
175175

176-
Please visit this link http://localhost:8000/import/, you shall see this upload form:
176+
Please visit this link http://localhost:8000/polls/import/, you shall see this upload form:
177177

178178
.. image:: import-page.png
179179

@@ -236,7 +236,7 @@ The custom formatting function is needed when the data from the excel sheet need
236236
Handle data export
237237
++++++++++++++++++++++++++++++
238238

239-
This section shows how to export the data in your models as an excel file. After you have completed the previous section, you can visit http://localhost:8000/export/book and you shall get a file download dialog:
239+
This section shows how to export the data in your models as an excel file. After you have completed the previous section, you can visit http://localhost:8000/polls/export/book and you shall get a file download dialog:
240240

241241
.. image:: download-dialog.png
242242

@@ -253,7 +253,24 @@ Now let's examine the code behind this in `polls/views.py <https://github.com/ch
253253
elif atype == "book":
254254
return excel.make_response_from_tables([Question, Choice], 'xls')
255255
256-
:meth:`~django_excel.make_response_from_tables` does all what is needed: read out the data, convert them into xls and give it the browser. And what you need to do is to give a list of models to be exported and a file type. As you have noticed, you can visit http://localhost:8000/exportsheet and will get **Question** exported as a single sheet file.
256+
:meth:`~django_excel.make_response_from_tables` does all what is needed: read out the data, convert them into xls and give it the browser. And what you need to do is to give a list of models to be exported and a file type. As you have noticed, you can visit http://localhost:8000/polls/export/sheet and will get **Question** exported as a single sheet file.
257+
258+
Handle custom data export
259+
+++++++++++++++++++++++++++++++
260+
261+
It is also quite common to download a portion of the data in a database table, for example the result of a search query. With version 0.0.2, you can pass on a query sets to to :meth:`~django_excel.make_response_from_query_sets` and generate an excel sheet from it::
262+
263+
def export_data(request, atype):
264+
...
265+
elif atype == "custom":
266+
question = Question.objects.get(slug='ide')
267+
query_sets = Choice.objects.filter(question=question)
268+
column_names = ['choice_text', 'id', 'votes']
269+
return excel.make_response_from_query_sets(query_sets, column_names, 'xls')
270+
271+
You can visit http://localhost:8000/polls/export/custom and will get the query set exported as a single sheet file as:
272+
273+
.. image:: custom-export.png
257274

258275
.. _data-types-and-its-conversion-funcs:
259276

@@ -262,18 +279,19 @@ All supported data types
262279

263280
Here is table of functions for all supported data types:
264281

265-
=========================== ======================================================== ==================================================
282+
=========================== ======================================================== ===================================================
266283
data structure from file to data structures from data structures to response
267-
=========================== ======================================================== ==================================================
284+
=========================== ======================================================== ===================================================
268285
dict :meth:`~django_excel.ExcelMixin.get_dict` :meth:`~django_excel.make_response_from_dict`
269286
records :meth:`~django_excel.ExcelMixin.get_records` :meth:`~django_excel.make_response_from_records`
270287
a list of lists :meth:`~django_excel.ExcelMixin.get_array` :meth:`~django_excel.make_response_from_array`
271288
dict of a list of lists :meth:`~django_excel.ExcelMixin.get_book_dict` :meth:`~django_excel.make_response_from_book_dict`
272-
:class:`~pyexcel.Sheet` :meth:`~django_excel.ExcelMixin.get_sheet` :meth:`~django_excel.make_response`
273-
:class:`~pyexcel.Book` :meth:`~django_excel.ExcelMixin.get_book` :meth:`~django_excel.make_response`
289+
:class:`pyexcel.Sheet` :meth:`~django_excel.ExcelMixin.get_sheet` :meth:`~django_excel.make_response`
290+
:class:`pyexcel.Book` :meth:`~django_excel.ExcelMixin.get_book` :meth:`~django_excel.make_response`
274291
database table :meth:`~django_excel.ExcelMixin.save_to_database` :meth:`~django_excel.make_response_from_a_table`
275-
a list of database tables :meth:`~django_excel.ExcelMixin.save_book_to_database` :meth:`~django_excel.make_response_from_tables`
276-
=========================== ======================================================== ==================================================
292+
a list of database tables :meth:`~django_excel.ExcelMixin.save_book_to_database` :meth:`~django_excel.make_response_from_tables`
293+
a database query sets :meth:`~django_excel.make_response_from_query_sets`
294+
=========================== ======================================================== ===================================================
277295

278296
See more examples of the data structures in :ref:`pyexcel documentation<pyexcel:a-list-of-data-structures>`
279297

@@ -291,7 +309,7 @@ API Reference
291309
:param sheet_name: For an excel book, there could be multiple sheets. If it is left
292310
unspecified, the sheet at index 0 is loaded. For 'csv', 'tsv' file,
293311
*sheet_name* should be None anyway.
294-
:param keywords: additional keywords to pyexcel library
312+
:param keywords: additional keywords to :meth:`pyexcel.get_sheet`
295313
:returns: A sheet object
296314

297315
.. method:: get_array(sheet_name=None, **keywords)
@@ -324,27 +342,19 @@ API Reference
324342
:param keywords: additional keywords to pyexcel library
325343
:returns: a two dimensional array, a list of lists
326344

327-
.. method:: save_to_database(table=None, **keywords)
328-
329-
:param table: a database table or a tuple which have this sequence (table, table_init_func, mapdict, name_columns_by_row, name_rows_by_column)
345+
.. method:: save_to_database(model=None, initializer=None, mapdict=None, **keywords)
330346

331-
==================== =================================================================================
332-
Field Description
333-
==================== =================================================================================
334-
table_init_funcs it is needed when your table had custom __init__ function
335-
mapdict model column names
336-
name_columns_by_row use a row to name columns. if you use name_rows_by_column, please set this to -1
337-
name_rows_by_column uses a column to name rows.
338-
==================== =================================================================================
347+
:param model: a django model
348+
:param initializer: a custom table initialization function if you have one
349+
:param mapdict: the explicit table column names if your excel data do not have the exact column names
350+
:param keywords: additional keywords to :meth:`pyexcel.Sheet.save_to_django_model`
339351

340-
:param keywords: additional keywords to pyexcel library
341-
342-
343-
.. method:: save_book_to_database(tables=None, **keywords)
344-
345-
:param tables: a list of database tables or tuples which have this sequence (table, table_init_func, mapdict, name_columns_by_row, name_rows_by_column), see :meth:`~ExcelMixin.save_to_database`
346-
:param keywords: additional keywords to pyexcel library
352+
.. method:: save_book_to_database(models=None, initializers=None, mapdicts=None, **keywords)
347353

354+
:param models: a list of django models
355+
:param initializers: a list of model initialization functions.
356+
:param mapdicts: a list of explicit table column names if your excel data sheets do not have the exact column names
357+
:param keywords: additional keywords to :meth:`pyexcel.Book.save_to_django_models`
348358

349359
Response methods
350360
-----------------
@@ -353,7 +363,7 @@ Response methods
353363

354364
.. method:: make_response(pyexcel_instance, file_type, status=200)
355365

356-
:param pyexcel_instance: pyexcel.Sheet or pyexcel.Book
366+
:param pyexcel_instance: :class:`pyexcel.Sheet` or :class:`pyexcel.Book`
357367
:param file_type: one of the following strings:
358368

359369
* 'csv'
@@ -393,9 +403,19 @@ Response methods
393403
:param status: same as :meth:`~django_excel.make_response`
394404

395405
.. autofunction:: make_response_from_a_table(model, file_type status=200)
396-
.. autofunction:: make_response_from_tables(models, file_type status=200)
397406

398407

408+
.. method:: make_response_from_query_sets(query_sets, column_names, file_type status=200)
409+
410+
Produce a single sheet Excel book of *file_type* from your custom database queries
411+
412+
:param query_sets: a query set
413+
:param column_names: a nominated column names. It could not be None, otherwise no data is returned.
414+
:param file_type: same as :meth:`~django_excel.make_response`
415+
:param status: same as :meth:`~django_excel.make_response`
416+
417+
.. autofunction:: make_response_from_tables(models, file_type status=200)
418+
399419
Indices and tables
400420
--------------------
401421

polls/views.py

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,12 @@ def export_data(request, atype):
5050
return excel.make_response_from_a_table(Question, 'xls')
5151
elif atype == "book":
5252
return excel.make_response_from_tables([Question, Choice], 'xls')
53-
53+
elif atype == "custom":
54+
question = Question.objects.get(slug='ide')
55+
query_sets = Choice.objects.filter(question=question)
56+
column_names = ['choice_text', 'id', 'votes']
57+
return excel.make_response_from_query_sets(query_sets, column_names, 'xls')
58+
5459
def import_data(request):
5560
if request.method == "POST":
5661
form = UploadFileForm(request.POST, request.FILES)
@@ -60,9 +65,11 @@ def choice_func(row):
6065
return row
6166
if form.is_valid():
6267
request.FILES['file'].save_book_to_database(
63-
models=[
64-
(Question, ['question_text', 'pub_date', 'slug'], None, 0),
65-
(Choice, ['question', 'choice_text', 'votes'], choice_func, 0)
68+
models=[Question, Choice],
69+
initializers=[None, choice_func],
70+
mapdicts=[
71+
['question_text', 'pub_date', 'slug'],
72+
['question', 'choice_text', 'votes']
6673
]
6774
)
6875
return HttpResponse("OK", status=200)
@@ -85,8 +92,8 @@ def import_dataa(request):
8592
form = UploadFileForm(request.POST, request.FILES)
8693
if form.is_valid():
8794
request.FILES['file'].save_to_database(
88-
model=(Question,
89-
['question_text', 'pub_date']))
95+
model=Question,
96+
mapdict=['question_text', 'pub_date'])
9097
return HttpResponse("OK")
9198
else:
9299
return HttpResponseBadRequest()

setup.py

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,25 +9,26 @@
99
README_txt = readme.read()
1010

1111
dependencies = [
12-
'pyexcel>=0.1.3',
12+
'pyexcel>=0.1.5',
13+
'pyexcel-webio>=0.0.2',
1314
'Django>=1.7.1'
1415
]
1516

1617
setup(
1718
name='django-excel',
1819
author="C. W.",
19-
version='0.0.1',
20+
version='0.0.2',
2021
author_email="wangc_2011@hotmail.com",
2122
url="https://github.com/chfw/django-excel",
22-
description='A django library to read, manipulate and write data in different excel formats: csv, ods, xls, xlsx and xlsm.',
23+
description='A django middleware that provides one application programming interface to read and write data in different excel file formats',
2324
install_requires=dependencies,
2425
packages=find_packages(exclude=['ez_setup', 'examples', 'tests', 'mysite', 'polls', 'migrations']),
2526
include_package_data=True,
2627
long_description=README_txt,
2728
zip_safe=False,
2829
tests_require=['nose'],
2930
keywords=['API', 'Django', 'Excel'],
30-
license='GNU GPLv3 or BSD',
31+
license='New BSD',
3132
classifiers=[
3233
'Development Status :: 3 - Alpha',
3334
'Environment :: Web Environment',

testResponse.py

Lines changed: 26 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ def test_download(self):
5555
print(file_type)
5656
response = self.client.get("/polls/download/"+file_type)
5757
assert response['Content-Type'] == FILE_TYPE_MIME_TABLE[file_type]
58-
sheet = pe.load_from_memory(file_type, response.content)
58+
sheet = pe.get_sheet(file_type=file_type, content=response.content)
5959
sheet.format(int)
6060
array = sheet.to_array()
6161
assert array == self.data
@@ -106,7 +106,7 @@ def test_exchange(self):
106106
response = self.client.post('/polls/exchange/'+file_type,
107107
data={"file": fp})
108108
assert response['Content-Type'] == FILE_TYPE_MIME_TABLE[file_type]
109-
sheet = pe.load_from_memory(file_type, response.content)
109+
sheet = pe.get_sheet(file_type=file_type, content=response.content)
110110
sheet.format(int)
111111
array = sheet.to_array()
112112
assert array == self.data
@@ -125,7 +125,7 @@ def testBook(self):
125125
assert response.status_code == 200
126126
response2 = self.client.get('/polls/export/book')
127127
assert response2.status_code == 200
128-
book = pe.load_book_from_memory('xls', response2.content)
128+
book = pe.get_book(file_type='xls', content=response2.content)
129129
content = dedent("""
130130
Sheet Name: question
131131
+----+---------------------------+----------------------------------------------+----------+
@@ -161,7 +161,7 @@ def testSheet(self):
161161
assert response.status_code == 200
162162
response2 = self.client.get('/polls/export/sheet')
163163
assert response2.status_code == 200
164-
sheet = pe.load_from_memory('xls', response2.content)
164+
sheet = pe.get_sheet(file_type='xls', content=response2.content)
165165
content = dedent("""
166166
Sheet Name: question
167167
+----+---------------------------+----------------------------------------------+----------+
@@ -173,6 +173,28 @@ def testSheet(self):
173173
+----+---------------------------+----------------------------------------------+----------+""").strip('\n')
174174
assert str(sheet) == content
175175

176+
def testCustomExport(self):
177+
fp = open(self.testfile, "rb")
178+
response = self.client.post('/polls/import/', data={"file": fp})
179+
assert response.status_code == 200
180+
response2 = self.client.get('/polls/export/custom')
181+
assert response2.status_code == 200
182+
sheet = pe.get_sheet(file_type='xls', content=response2.content)
183+
content = dedent("""
184+
Sheet Name: pyexcel_sheet1
185+
+---------------+----+-------+
186+
| choice_text | id | votes |
187+
+---------------+----+-------+
188+
| Eclipse | 4 | 0 |
189+
+---------------+----+-------+
190+
| Visual Studio | 5 | 0 |
191+
+---------------+----+-------+
192+
| PyCharm | 6 | 0 |
193+
+---------------+----+-------+
194+
| IntelliJ | 7 | 0 |
195+
+---------------+----+-------+""").strip('\n')
196+
assert str(sheet) == content
197+
176198

177199
@override_settings(FILE_UPLOAD_MAX_MEMORY_SIZE=1)
178200
class ExcelResponseUsingFileTestCase(ExcelResponseTestCase):

0 commit comments

Comments
 (0)