├── .github ├── FUNDING.yml ├── PULL_REQUEST_TEMPLATE.md └── workflows │ ├── lint.yml │ ├── moban-update.yml │ ├── pythonpublish.yml │ └── tests.yml ├── .gitignore ├── .moban.d ├── MANIFEST.in ├── custom_README.rst.jj2 ├── custom_setup.py.jj2 ├── custom_travis.yml.jj2 ├── docs │ └── source │ │ ├── conf.py │ │ └── django-index.rst.jj2 ├── requirements.txt ├── setup.rst.jj2 ├── tests │ └── custom_requirements.txt.jj2 └── verified_frameworks.rst.jj2 ├── .moban.yml ├── .readthedocs.yml ├── .travis.yml ├── CHANGELOG.rst ├── CONTRIBUTORS.rst ├── LICENSE ├── MANIFEST.in ├── Makefile ├── README.rst ├── _compact.py ├── changelog.yml ├── db.sqlite3 ├── django_excel.yml ├── django_excel ├── __init__.py └── _compact.py ├── docs ├── Makefile ├── make.bat ├── requirements.txt └── source │ ├── _static │ ├── handsontable.full.min.css │ └── handsontable.full.min.js │ ├── _themes │ ├── djangodocs-epub │ │ ├── epub-cover.html │ │ ├── static │ │ │ ├── docicons-behindscenes.png │ │ │ ├── docicons-note.png │ │ │ ├── docicons-philosophy.png │ │ │ ├── docicons-warning.png │ │ │ └── epub.css │ │ └── theme.conf │ └── djangodocs │ │ ├── genindex.html │ │ ├── layout.html │ │ ├── modindex.html │ │ ├── search.html │ │ ├── static │ │ ├── default.css │ │ ├── djangodocs.css │ │ ├── docicons-behindscenes.png │ │ ├── docicons-note.png │ │ ├── docicons-philosophy.png │ │ ├── docicons-warning.png │ │ ├── homepage.css │ │ └── reset-fonts-grids.css │ │ └── theme.conf │ ├── admin-vote.png │ ├── choice-admin.png │ ├── choice-sheet.png │ ├── conf.py │ ├── contents.rst │ ├── custom-export.png │ ├── download-dialog.png │ ├── download-file.png │ ├── handsontable-choice.png │ ├── handsontable-embedded.png │ ├── handsontable-question.png │ ├── import-page.png │ ├── index.rst │ ├── question-admin.png │ ├── question-sheet.png │ ├── spelling_wordlist.txt │ ├── survey-result.png │ ├── upload-form.png │ └── votes-handson-table.png ├── format.sh ├── lint.sh ├── manage.py ├── mysite ├── __init__.py ├── settings.py ├── urls.py └── wsgi.py ├── polls ├── __init__.py ├── admin.py ├── models.py ├── urls.py └── views.py ├── requirements.txt ├── rnd_requirements.txt ├── sample-data.xls ├── sample-sheet-for-isave.xls ├── sample-sheet.xls ├── setup.cfg ├── setup.py ├── templates ├── custom-handson-table.html ├── handsontable.html ├── survey_result.html └── upload_form.html ├── test.bat ├── test.sh ├── testResponse.py └── tests └── requirements.txt /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: chfw 4 | patreon: chfw 5 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | With your PR, here is a check list: 2 | 3 | - [ ] Has test cases written? 4 | - [ ] Has all code lines tested? 5 | - [ ] Has `make format` been run? 6 | - [ ] Please update CHANGELOG.yml(not CHANGELOG.rst) 7 | - [ ] Has fair amount of documentation if your change is complex 8 | - [ ] Agree on NEW BSD License for your contribution 9 | -------------------------------------------------------------------------------- /.github/workflows/lint.yml: -------------------------------------------------------------------------------- 1 | name: lint 2 | 3 | on: [push, pull_request] 4 | 5 | jobs: 6 | lint: 7 | runs-on: ubuntu-latest 8 | name: lint code 9 | steps: 10 | - uses: actions/checkout@v2 11 | - name: Set up Python 12 | uses: actions/setup-python@v1 13 | with: 14 | python-version: 3.8 15 | - name: lint 16 | run: | 17 | pip --use-deprecated=legacy-resolver install flake8 18 | pip --use-deprecated=legacy-resolver install -r tests/requirements.txt 19 | flake8 --exclude=.moban.d,docs,setup.py --builtins=unicode,xrange,long . 20 | python setup.py checkdocs 21 | -------------------------------------------------------------------------------- /.github/workflows/moban-update.yml: -------------------------------------------------------------------------------- 1 | on: [push] 2 | 3 | jobs: 4 | run_moban: 5 | runs-on: ubuntu-latest 6 | name: synchronize templates via moban 7 | steps: 8 | - uses: actions/checkout@v2 9 | with: 10 | ref: ${{ github.head_ref }} 11 | token: ${{ secrets.PAT }} 12 | - name: Set up Python 13 | uses: actions/setup-python@v1 14 | with: 15 | python-version: '3.7' 16 | - name: check changes 17 | run: | 18 | pip install markupsafe==2.0.1 19 | pip install moban gitfs2 pypifs moban-jinja2-github moban-ansible 20 | moban 21 | git status 22 | git diff --exit-code 23 | - name: Auto-commit 24 | if: failure() 25 | uses: stefanzweifel/git-auto-commit-action@v4 26 | with: 27 | commit_message: >- 28 | This is an auto-commit, updating project meta data, 29 | such as changelog.rst, contributors.rst 30 | -------------------------------------------------------------------------------- /.github/workflows/pythonpublish.yml: -------------------------------------------------------------------------------- 1 | name: Upload Python Package 2 | 3 | on: 4 | release: 5 | types: [created] 6 | 7 | jobs: 8 | pypi-publish: 9 | name: upload release to PyPI 10 | runs-on: ubuntu-latest 11 | # Specifying a GitHub environment is optional, but strongly encouraged 12 | environment: pypi 13 | permissions: 14 | # IMPORTANT: this permission is mandatory for trusted publishing 15 | id-token: write 16 | steps: 17 | # retrieve your distributions here 18 | - uses: actions/checkout@v1 19 | - name: Set up Python 20 | uses: actions/setup-python@v1 21 | with: 22 | python-version: '3.x' 23 | - name: Install dependencies 24 | run: | 25 | python -m pip install --upgrade pip 26 | pip install setuptools wheel 27 | - name: Build 28 | run: | 29 | python setup.py sdist bdist_wheel 30 | - name: Publish package distributions to PyPI 31 | uses: pypa/gh-action-pypi-publish@release/v1 32 | -------------------------------------------------------------------------------- /.github/workflows/tests.yml: -------------------------------------------------------------------------------- 1 | name: Run unit tests on Windows, Ubuntu and Mac 2 | 3 | on: [push, pull_request] 4 | 5 | jobs: 6 | 7 | test: 8 | name: ${{ matrix.os }} / ${{ matrix.python_version }} 9 | runs-on: ${{ matrix.os }}-latest 10 | strategy: 11 | fail-fast: false 12 | matrix: 13 | os: [Ubuntu] 14 | python_version: ["3.9.16"] 15 | 16 | steps: 17 | - uses: actions/checkout@v2 18 | - name: Set up Python 19 | uses: actions/setup-python@v5 20 | with: 21 | python-version: ${{ matrix.python_version }} 22 | architecture: x64 23 | 24 | - name: install 25 | run: | 26 | pip --use-deprecated=legacy-resolver install -r requirements.txt 27 | pip --use-deprecated=legacy-resolver install -r tests/requirements.txt 28 | - name: test 29 | run: | 30 | pip freeze 31 | nosetests --verbosity=3 --with-coverage --cover-package django_excel --cover-package tests tests --with-doctest --doctest-extension=.rst README.rst docs/source django_excel 32 | - name: Upload coverage 33 | uses: codecov/codecov-action@v1 34 | with: 35 | name: ${{ matrix.os }} Python ${{ matrix.python-version }} -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # moban hashes 2 | .moban.hashes 3 | 4 | # Extra rules from https://github.com/github/gitignore/ 5 | # Python rules 6 | # Byte-compiled / optimized / DLL files 7 | __pycache__/ 8 | *.py[cod] 9 | *$py.class 10 | 11 | # C extensions 12 | *.so 13 | 14 | # Distribution / packaging 15 | .Python 16 | build/ 17 | develop-eggs/ 18 | dist/ 19 | downloads/ 20 | eggs/ 21 | .eggs/ 22 | lib/ 23 | lib64/ 24 | parts/ 25 | sdist/ 26 | var/ 27 | wheels/ 28 | share/python-wheels/ 29 | *.egg-info/ 30 | .installed.cfg 31 | *.egg 32 | MANIFEST 33 | 34 | # PyInstaller 35 | # Usually these files are written by a python script from a template 36 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 37 | *.manifest 38 | *.spec 39 | 40 | # Installer logs 41 | pip-log.txt 42 | pip-delete-this-directory.txt 43 | 44 | # Unit test / coverage reports 45 | htmlcov/ 46 | .tox/ 47 | .nox/ 48 | .coverage 49 | .coverage.* 50 | .cache 51 | nosetests.xml 52 | coverage.xml 53 | *.cover 54 | *.py,cover 55 | .hypothesis/ 56 | .pytest_cache/ 57 | cover/ 58 | 59 | # Translations 60 | *.mo 61 | *.pot 62 | 63 | # Django stuff: 64 | *.log 65 | local_settings.py 66 | db.sqlite3 67 | db.sqlite3-journal 68 | 69 | # Flask stuff: 70 | instance/ 71 | .webassets-cache 72 | 73 | # Scrapy stuff: 74 | .scrapy 75 | 76 | # Sphinx documentation 77 | docs/_build/ 78 | 79 | # PyBuilder 80 | .pybuilder/ 81 | target/ 82 | 83 | # Jupyter Notebook 84 | .ipynb_checkpoints 85 | 86 | # IPython 87 | profile_default/ 88 | ipython_config.py 89 | 90 | # pyenv 91 | # For a library or package, you might want to ignore these files since the code is 92 | # intended to run in multiple environments; otherwise, check them in: 93 | # .python-version 94 | 95 | # pipenv 96 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. 97 | # However, in case of collaboration, if having platform-specific dependencies or dependencies 98 | # having no cross-platform support, pipenv may install dependencies that don't work, or not 99 | # install all needed dependencies. 100 | #Pipfile.lock 101 | 102 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow 103 | __pypackages__/ 104 | 105 | # Celery stuff 106 | celerybeat-schedule 107 | celerybeat.pid 108 | 109 | # SageMath parsed files 110 | *.sage.py 111 | 112 | # Environments 113 | .env 114 | .venv 115 | env/ 116 | venv/ 117 | ENV/ 118 | env.bak/ 119 | venv.bak/ 120 | 121 | # Spyder project settings 122 | .spyderproject 123 | .spyproject 124 | 125 | # Rope project settings 126 | .ropeproject 127 | 128 | # mkdocs documentation 129 | /site 130 | 131 | # mypy 132 | .mypy_cache/ 133 | .dmypy.json 134 | dmypy.json 135 | 136 | # Pyre type checker 137 | .pyre/ 138 | 139 | # pytype static type analyzer 140 | .pytype/ 141 | 142 | # Cython debug symbols 143 | cython_debug/ 144 | 145 | # VirtualEnv rules 146 | # Virtualenv 147 | # http://iamzed.com/2009/05/07/a-primer-on-virtualenv/ 148 | .Python 149 | [Bb]in 150 | [Ii]nclude 151 | [Ll]ib 152 | [Ll]ib64 153 | [Ll]ocal 154 | [Ss]cripts 155 | pyvenv.cfg 156 | .venv 157 | pip-selfcheck.json 158 | 159 | # Linux rules 160 | *~ 161 | 162 | # temporary files which can be created if a process still has a handle open of a deleted file 163 | .fuse_hidden* 164 | 165 | # KDE directory preferences 166 | .directory 167 | 168 | # Linux trash folder which might appear on any partition or disk 169 | .Trash-* 170 | 171 | # .nfs files are created when an open file is removed but is still being accessed 172 | .nfs* 173 | 174 | # Windows rules 175 | # Windows thumbnail cache files 176 | Thumbs.db 177 | Thumbs.db:encryptable 178 | ehthumbs.db 179 | ehthumbs_vista.db 180 | 181 | # Dump file 182 | *.stackdump 183 | 184 | # Folder config file 185 | [Dd]esktop.ini 186 | 187 | # Recycle Bin used on file shares 188 | $RECYCLE.BIN/ 189 | 190 | # Windows Installer files 191 | *.cab 192 | *.msi 193 | *.msix 194 | *.msm 195 | *.msp 196 | 197 | # Windows shortcuts 198 | *.lnk 199 | 200 | # macOS rules 201 | # General 202 | .DS_Store 203 | .AppleDouble 204 | .LSOverride 205 | 206 | # Icon must end with two \r 207 | Icon 208 | 209 | 210 | # Thumbnails 211 | ._* 212 | 213 | # Files that might appear in the root of a volume 214 | .DocumentRevisions-V100 215 | .fseventsd 216 | .Spotlight-V100 217 | .TemporaryItems 218 | .Trashes 219 | .VolumeIcon.icns 220 | .com.apple.timemachine.donotpresent 221 | 222 | # Directories potentially created on remote AFP share 223 | .AppleDB 224 | .AppleDesktop 225 | Network Trash Folder 226 | Temporary Items 227 | .apdisk 228 | 229 | # Emacs rules 230 | # -*- mode: gitignore; -*- 231 | *~ 232 | \#*\# 233 | /.emacs.desktop 234 | /.emacs.desktop.lock 235 | *.elc 236 | auto-save-list 237 | tramp 238 | .\#* 239 | 240 | # Org-mode 241 | .org-id-locations 242 | *_archive 243 | 244 | # flymake-mode 245 | *_flymake.* 246 | 247 | # eshell files 248 | /eshell/history 249 | /eshell/lastdir 250 | 251 | # elpa packages 252 | /elpa/ 253 | 254 | # reftex files 255 | *.rel 256 | 257 | # AUCTeX auto folder 258 | /auto/ 259 | 260 | # cask packages 261 | .cask/ 262 | dist/ 263 | 264 | # Flycheck 265 | flycheck_*.el 266 | 267 | # server auth directory 268 | /server/ 269 | 270 | # projectiles files 271 | .projectile 272 | 273 | # directory configuration 274 | .dir-locals.el 275 | 276 | # network security 277 | /network-security.data 278 | 279 | 280 | # Vim rules 281 | # Swap 282 | [._]*.s[a-v][a-z] 283 | !*.svg # comment out if you don't need vector files 284 | [._]*.sw[a-p] 285 | [._]s[a-rt-v][a-z] 286 | [._]ss[a-gi-z] 287 | [._]sw[a-p] 288 | 289 | # Session 290 | Session.vim 291 | Sessionx.vim 292 | 293 | # Temporary 294 | .netrwhist 295 | *~ 296 | # Auto-generated tag files 297 | tags 298 | # Persistent undo 299 | [._]*.un~ 300 | 301 | # JetBrains rules 302 | # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider 303 | # Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 304 | 305 | # User-specific stuff 306 | .idea/**/workspace.xml 307 | .idea/**/tasks.xml 308 | .idea/**/usage.statistics.xml 309 | .idea/**/dictionaries 310 | .idea/**/shelf 311 | 312 | # Generated files 313 | .idea/**/contentModel.xml 314 | 315 | # Sensitive or high-churn files 316 | .idea/**/dataSources/ 317 | .idea/**/dataSources.ids 318 | .idea/**/dataSources.local.xml 319 | .idea/**/sqlDataSources.xml 320 | .idea/**/dynamic.xml 321 | .idea/**/uiDesigner.xml 322 | .idea/**/dbnavigator.xml 323 | 324 | # Gradle 325 | .idea/**/gradle.xml 326 | .idea/**/libraries 327 | 328 | # Gradle and Maven with auto-import 329 | # When using Gradle or Maven with auto-import, you should exclude module files, 330 | # since they will be recreated, and may cause churn. Uncomment if using 331 | # auto-import. 332 | # .idea/artifacts 333 | # .idea/compiler.xml 334 | # .idea/jarRepositories.xml 335 | # .idea/modules.xml 336 | # .idea/*.iml 337 | # .idea/modules 338 | # *.iml 339 | # *.ipr 340 | 341 | # CMake 342 | cmake-build-*/ 343 | 344 | # Mongo Explorer plugin 345 | .idea/**/mongoSettings.xml 346 | 347 | # File-based project format 348 | *.iws 349 | 350 | # IntelliJ 351 | out/ 352 | 353 | # mpeltonen/sbt-idea plugin 354 | .idea_modules/ 355 | 356 | # JIRA plugin 357 | atlassian-ide-plugin.xml 358 | 359 | # Cursive Clojure plugin 360 | .idea/replstate.xml 361 | 362 | # Crashlytics plugin (for Android Studio and IntelliJ) 363 | com_crashlytics_export_strings.xml 364 | crashlytics.properties 365 | crashlytics-build.properties 366 | fabric.properties 367 | 368 | # Editor-based Rest Client 369 | .idea/httpRequests 370 | 371 | # Android studio 3.1+ serialized cache file 372 | .idea/caches/build_file_checksums.ser 373 | 374 | # SublimeText rules 375 | # Cache files for Sublime Text 376 | *.tmlanguage.cache 377 | *.tmPreferences.cache 378 | *.stTheme.cache 379 | 380 | # Workspace files are user-specific 381 | *.sublime-workspace 382 | 383 | # Project files should be checked into the repository, unless a significant 384 | # proportion of contributors will probably not be using Sublime Text 385 | # *.sublime-project 386 | 387 | # SFTP configuration file 388 | sftp-config.json 389 | sftp-config-alt*.json 390 | 391 | # Package control specific files 392 | Package Control.last-run 393 | Package Control.ca-list 394 | Package Control.ca-bundle 395 | Package Control.system-ca-bundle 396 | Package Control.cache/ 397 | Package Control.ca-certs/ 398 | Package Control.merged-ca-bundle 399 | Package Control.user-ca-bundle 400 | oscrypto-ca-bundle.crt 401 | bh_unicode_properties.cache 402 | 403 | # Sublime-github package stores a github token in this file 404 | # https://packagecontrol.io/packages/sublime-github 405 | GitHub.sublime-settings 406 | 407 | # KDevelop4 rules 408 | *.kdev4 409 | .kdev4/ 410 | 411 | # Kate rules 412 | # Swap Files # 413 | .*.kate-swp 414 | .swp.* 415 | 416 | # TextMate rules 417 | *.tmproj 418 | *.tmproject 419 | tmtags 420 | 421 | # VisualStudioCode rules 422 | .vscode/* 423 | !.vscode/settings.json 424 | !.vscode/tasks.json 425 | !.vscode/launch.json 426 | !.vscode/extensions.json 427 | *.code-workspace 428 | 429 | # Local History for Visual Studio Code 430 | .history/ 431 | 432 | # Xcode rules 433 | # Xcode 434 | # 435 | # gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore 436 | 437 | ## User settings 438 | xcuserdata/ 439 | 440 | ## compatibility with Xcode 8 and earlier (ignoring not required starting Xcode 9) 441 | *.xcscmblueprint 442 | *.xccheckout 443 | 444 | ## compatibility with Xcode 3 and earlier (ignoring not required starting Xcode 4) 445 | build/ 446 | DerivedData/ 447 | *.moved-aside 448 | *.pbxuser 449 | !default.pbxuser 450 | *.mode1v3 451 | !default.mode1v3 452 | *.mode2v3 453 | !default.mode2v3 454 | *.perspectivev3 455 | !default.perspectivev3 456 | 457 | ## Gcc Patch 458 | /*.gcno 459 | 460 | # Eclipse rules 461 | .metadata 462 | bin/ 463 | tmp/ 464 | *.tmp 465 | *.bak 466 | *.swp 467 | *~.nib 468 | local.properties 469 | .settings/ 470 | .loadpath 471 | .recommenders 472 | 473 | # External tool builders 474 | .externalToolBuilders/ 475 | 476 | # Locally stored "Eclipse launch configurations" 477 | *.launch 478 | 479 | # PyDev specific (Python IDE for Eclipse) 480 | *.pydevproject 481 | 482 | # CDT-specific (C/C++ Development Tooling) 483 | .cproject 484 | 485 | # CDT- autotools 486 | .autotools 487 | 488 | # Java annotation processor (APT) 489 | .factorypath 490 | 491 | # PDT-specific (PHP Development Tools) 492 | .buildpath 493 | 494 | # sbteclipse plugin 495 | .target 496 | 497 | # Tern plugin 498 | .tern-project 499 | 500 | # TeXlipse plugin 501 | .texlipse 502 | 503 | # STS (Spring Tool Suite) 504 | .springBeans 505 | 506 | # Code Recommenders 507 | .recommenders/ 508 | 509 | # Annotation Processing 510 | .apt_generated/ 511 | .apt_generated_test/ 512 | 513 | # Scala IDE specific (Scala & Java development for Eclipse) 514 | .cache-main 515 | .scala_dependencies 516 | .worksheet 517 | 518 | # Uncomment this line if you wish to ignore the project description file. 519 | # Typically, this file would be tracked if it contains build/dependency configurations: 520 | #.project 521 | 522 | # TortoiseGit rules 523 | # Project-level settings 524 | /.tgitconfig 525 | 526 | # Tags rules 527 | # Ignore tags created by etags, ctags, gtags (GNU global) and cscope 528 | TAGS 529 | .TAGS 530 | !TAGS/ 531 | tags 532 | .tags 533 | !tags/ 534 | gtags.files 535 | GTAGS 536 | GRTAGS 537 | GPATH 538 | GSYMS 539 | cscope.files 540 | cscope.out 541 | cscope.in.out 542 | cscope.po.out 543 | 544 | 545 | # remove moban hash dictionary 546 | .moban.hashes 547 | -------------------------------------------------------------------------------- /.moban.d/MANIFEST.in: -------------------------------------------------------------------------------- 1 | {%extends 'MANIFEST.in.jj2'%} 2 | 3 | {%block more_options%} 4 | prune mysite 5 | prune polls 6 | {%endblock%} 7 | -------------------------------------------------------------------------------- /.moban.d/custom_README.rst.jj2: -------------------------------------------------------------------------------- 1 | {%extends 'WEB-README.rst.jj2' %} 2 | 3 | {%block header %} 4 | {%include 'verified_frameworks.rst.jj2'%} 5 | {%endblock%} 6 | 7 | {%block setup%} 8 | Setup 9 | ====== 10 | 11 | {%include "setup.rst.jj2"%} 12 | 13 | {%endblock%} 14 | 15 | 16 | {% block usage %} 17 | Usage 18 | ========= 19 | Here is the example viewing function codes: 20 | 21 | .. code-block:: python 22 | 23 | from django.shortcuts import render_to_response 24 | from django.http import HttpResponseBadRequest 25 | from django import forms 26 | from django.template import RequestContext 27 | import django_excel as excel 28 | 29 | class UploadFileForm(forms.Form): 30 | file = forms.FileField() 31 | 32 | def upload(request): 33 | if request.method == "POST": 34 | form = UploadFileForm(request.POST, request.FILES) 35 | if form.is_valid(): 36 | filehandle = request.FILES['file'] 37 | return excel.make_response(filehandle.get_sheet(), "csv") 38 | else: 39 | return HttpResponseBadRequest() 40 | else: 41 | form = UploadFileForm() 42 | return render_to_response('upload_form.html', 43 | {'form': form}, 44 | context_instance=RequestContext(request)) 45 | 46 | def download(request): 47 | sheet = excel.pe.Sheet([[1, 2],[3, 4]]) 48 | return excel.make_response(sheet, "csv") 49 | {% endblock %} 50 | -------------------------------------------------------------------------------- /.moban.d/custom_setup.py.jj2: -------------------------------------------------------------------------------- 1 | {% extends 'pyexcel-setup.py.jj2' %} 2 | 3 | {%block platform_block%} 4 | {%endblock%} 5 | 6 | {%block extras %} 7 | {%endblock %} 8 | 9 | {%block pyexcel_extra_classifiers%} 10 | 'Development Status :: 3 - Alpha', 11 | 'Environment :: Web Environment', 12 | 'Topic :: Internet :: WWW/HTTP', 13 | 'Topic :: Software Development :: Libraries :: Python Modules', 14 | 'Framework :: Django :: 1.7', 15 | 'Framework :: Django :: 1.8', 16 | 'Framework :: Django :: 1.9', 17 | 'Programming Language :: Python :: 2', 18 | 'Programming Language :: Python :: 2.7', 19 | 'Programming Language :: Python :: 3', 20 | 'Programming Language :: Python :: 3.3', 21 | 'Programming Language :: Python :: 3.4', 22 | 'Programming Language :: Python :: 3.5' 23 | {%endblock%} 24 | -------------------------------------------------------------------------------- /.moban.d/custom_travis.yml.jj2: -------------------------------------------------------------------------------- 1 | {% extends "travis.yml.jj2" %} 2 | {%block test_other_environments%} 3 | env: 4 | - DJANGO_VERSION=1.8.18 5 | - DJANGO_VERSION=1.9.13 6 | - DJANGO_VERSION=1.10.8 7 | - DJANGO_VERSION=1.11.15 8 | - DJANGO_VERSION=2.0.8 9 | - DJANGO_VERSION=2.1 10 | - DJANGO_VERSION=3.1.2 11 | {%endblock%}1 12 | 13 | {%block extra_jobs%} 14 | exclude: 15 | - python: 3.6 16 | env: DJANGO_VERSION=1.9.13 17 | - python: 3.6 18 | env: DJANGO_VERSION=1.10.8 19 | - python: 3.7 20 | env: DJANGO_VERSION=1.8.18 21 | - python: 3.7 22 | env: DJANGO_VERSION=1.9.13 23 | - python: 3.7 24 | env: DJANGO_VERSION=1.10.8 25 | - python: 3.7 26 | env: DJANGO_VERSION=1.8.18 27 | - python: 3.7 28 | env: DJANGO_VERSION=1.11.15 29 | {%endblock%} 30 | 31 | {%block custom_python_versions%} 32 | python: 33 | - 3.6 34 | - 3.7 35 | {%endblock%} 36 | 37 | {% block custom_install %} - if [ -n "$DJANGO_VERSION" ]; then 38 | pip install Django==$DJANGO_VERSION; 39 | fi 40 | {% endblock%} 41 | -------------------------------------------------------------------------------- /.moban.d/docs/source/conf.py: -------------------------------------------------------------------------------- 1 | {% extends 'docs/source/conf.py.jj2' %} 2 | 3 | {%block SPHINX_EXTENSIONS%} 4 | 'sphinxcontrib.excel' 5 | {%endblock%} 6 | 7 | 8 | {%block custom_doc_theme%} 9 | import os # noqa 10 | import sys # noqa 11 | sys.path.append(os.path.abspath('_themes')) 12 | html_theme_path = ['_themes'] 13 | html_theme = 'djangodocs' 14 | {%endblock%} 15 | -------------------------------------------------------------------------------- /.moban.d/docs/source/django-index.rst.jj2: -------------------------------------------------------------------------------- 1 | {% extends 'docs/source/WEB-index.rst.jj2' %} 2 | 3 | {% block further_dissertation %} 4 | Given the existence of pyexcel, what is the reason for django-excel? 5 | 1. **Speedy file uploads**. **django-excel** help you access the uploaded excel file directly using ExcelMemoryFileUploadHandler and TemporaryExcelFileUploadHandler. MemoryFileUploadHandler holds the uploaded file in memory and django-excel reads the excel data from this memory buffer without caching it onto file system. Meanwhile, TemporaryExcelFileUploadHandler holds the uploaded file in file system and django-excel reads directly from this stream-to-file without extra function calls. 6 | 2. **Import excel data into database**. **django-excel** uses bulk_insert to import your excel 7 | data into your django Model, which is very efficient. 8 | {% endblock %} 9 | 10 | 11 | {%block installation_note%} 12 | Contrary to Django's philosophy of 'battery included', django-excel does not 13 | come with all batteries due to the size of the dependency(xlwt, openpyxl, odfpy). Hence, 14 | Django developer is left with the choice to install and load the excel file formats. 15 | {%endblock%} 16 | 17 | {%block verified_frameworks%} 18 | Tested Django Versions 19 | -------------------------------------------------------------------------------- 20 | 21 | 2.1, 2.08, 1.11.15, 1.10.8, 1.9.13, 1.8.18, 1.7.11, 1.6.11 22 | 23 | Since 15 March 2015, python 2.6 are no longer tested via travis-ci. 24 | 25 | {%endblock%} 26 | 27 | 28 | {%block setup %} 29 | 30 | {%include "setup.rst.jj2"%} 31 | 32 | {%endblock%} 33 | 34 | {%block tutorial%} 35 | Tutorial 36 | -------------------------------------------------------------------------------- 37 | 38 | In order to dive in django-excel and get hands-on experience quickly, the test 39 | application for django-excel will be introduced here. So, it is advisable that 40 | you should check out the code from 41 | `github `_ :: 42 | 43 | git clone https://github.com/pyexcel/django-excel.git 44 | 45 | The test application is written according 46 | to `Part 1 `_, 47 | `Part 2 `_ and 48 | `Part 3 `_ of django 49 | tutorial. If you should wonder how the test application was written, please 50 | visit django documentation and come back. 51 | 52 | Once you have the code, please change to django-excel directory and then 53 | install all dependencies:: 54 | 55 | $ cd django-excel 56 | $ pip install -r requirements.txt 57 | $ pip install -r tests/requirements.txt 58 | 59 | Then run the test application:: 60 | 61 | $ python manage.py runserver 62 | Performing system checks... 63 | 64 | System check identified no issues (0 silenced). 65 | 66 | You have 9 unapplied migration(s). Your project may not work properly until you apply the migrations for app(s): admin, auth, contenttypes. 67 | Run 'python manage.py migrate' to apply them. 68 | 69 | July 06, 2017 - 08:29:10 70 | Django version 1.11.3, using settings 'mysite.settings' 71 | Starting development server at http://127.0.0.1:8000/ 72 | Quit the server with CONTROL-C. 73 | 74 | 75 | .. note:: 76 | 77 | The 9 unapplied migration(s) were ignored because migrations are out of scope in this 78 | tutorial. 79 | 80 | Handle excel file upload and download 81 | ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 82 | 83 | This example shows how to process uploaded excel file and how to make data 84 | download as an excel file. Open your browser 85 | and visit http://localhost:8000/polls/, you shall see this upload form: 86 | 87 | .. image :: upload-form.png 88 | 89 | Choose an excel sheet, for example an xls file, and press "Submit". You will get a csv file for download. 90 | 91 | .. image :: download-file.png 92 | 93 | Please open the file 94 | `polls/views.py `_ 95 | and focus on the following code section: 96 | 97 | .. literalinclude:: ../../polls/views.py 98 | :lines: 14-36 99 | 100 | 101 | **UploadFileForm** is html widget for file upload form in the html page. Then 102 | look down at **filehandle**. It is an instance of either ExcelInMemoryUploadedFile 103 | or TemporaryUploadedExcelFile, which inherit ExcelMixin and hence have a list of 104 | conversion methods to call, such as get_sheet, get_array, etc. 105 | 106 | For the response, :meth:`~django_excel.make_response` converts 107 | :class:`pyexcel.Sheet` instance obtained via 108 | :meth:`~django_excel.ExcelMixin.get_sheet` into a csv file for download. 109 | 110 | Please feel free to change those functions according to :ref:`the mapping table `. 111 | 112 | 113 | Handle data import 114 | ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 115 | 116 | This example shows how to import uploaded excel file into django models. We are 117 | going to import 118 | `sample-data.xls `_ 119 | 120 | .. pyexcel-table:: ../../sample-data.xls 121 | 122 | into the following data models: 123 | 124 | .. literalinclude:: ../../polls/models.py 125 | :lines: 4- 126 | 127 | .. note:: 128 | Except the added "slug" field, **Question** and **Choice** are copied from Django tutorial part 1. 129 | 130 | .. note:: 131 | Please also pay attention to 'choice' sheet. There exists an arbitrary column: "Noise", which 132 | exists to show case skipping column feature using mapdicts later. 133 | 134 | Please visit this link http://localhost:8000/polls/import/, you shall see this upload form: 135 | 136 | .. image:: import-page.png 137 | 138 | Please then select 139 | `sample-data.xls `_ 140 | and upload. And you get the following excel-alike table in response to confirm all were imported. 141 | 142 | .. image:: handsontable-question.png 143 | 144 | .. image:: handsontable-choice.png 145 | 146 | .. note :: 147 | 148 | pyexcel-handsontable along with pyexcel v0.5.0 brings excel-alie table rendering feature. 149 | Let me explain how this view is done a few paragraphs later. 150 | 151 | Then visit the admin page http://localhost:8000/admin/polls/question, 152 | you shall see questions have been populated: 153 | 154 | .. image:: question-admin.png 155 | 156 | .. note:: 157 | The admin user credentials are: user name: admin, password: admin 158 | 159 | And choices too: 160 | 161 | .. image:: choice-admin.png 162 | 163 | You may use admin interface to delete all those objects and try again. 164 | 165 | Now please open `polls/views.py `_ 166 | and focus on this part of code: 167 | 168 | .. literalinclude:: ../../polls/views.py 169 | :lines: 72-92 170 | 171 | The star is :meth:`~django_excel.save_book_to_database`. The parameter **models** 172 | should be a list of django models. **initializers** is a list of initialization 173 | functions for each model. In the example, we do not have init function for Question 174 | so 'None' is given and `choice_func` is given to Choice. **mapdicts** is a list of 175 | column names for each model and the member of the **mapdicts** can be a dictionary:: 176 | 177 | { 178 | "Question Text": "question_text", 179 | "Publish Date": "pub_date", 180 | "Unique Identifier": "slug" 181 | } 182 | 183 | As a dictionary, it can be used to skip columns in the incoming sheets. For example, 184 | 'Noise' column is skipped because it was not mentioned in the mapdict. 185 | 186 | The custom initialization function is needed when the data from the excel sheet 187 | needs translation before data import. For example, **Choice** has a foreign 188 | key to **Question**. When choice data are to be imported, "Question" column 189 | needs to be translated to a question instance. In our example, "Question" column 190 | in "Sheet 2" contains the values appeared in "Unique Identifier" column in 191 | "Sheet 1". 192 | 193 | 194 | Handle data export 195 | ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 196 | 197 | This section shows how to export the data in your models as an excel file. After 198 | you have completed the previous section, you can visit 199 | http://localhost:8000/polls/export/book and you shall get a file download dialog: 200 | 201 | .. image:: download-dialog.png 202 | 203 | Please save and open it. You shall see these data in your window: 204 | 205 | .. image:: question-sheet.png 206 | .. image:: choice-sheet.png 207 | 208 | Now let's examine the code behind this in 209 | `polls/views.py `_: 210 | 211 | .. literalinclude:: ../../polls/views.py 212 | :lines: 49-56 213 | 214 | 215 | :meth:`~django_excel.make_response_from_tables` does all what is needed: read out 216 | the data, convert them into xls and give it the browser. And what you need to do 217 | is to give a list of models to be exported and a file type. As you have noticed, 218 | you can visit http://localhost:8000/polls/export/sheet and will get **Question** 219 | exported as a single sheet file. 220 | 221 | 222 | Render an excel-alike html in a browser 223 | ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 224 | 225 | In previous section, you have seen the rendering of the excel-alike table. First of 226 | all, the credits goes to `handsontable developers `_. 227 | `pyexcel-handsontable`_ as renderer plugin to pyexcel v0.5.0 bring it to 228 | pyexcel developers. 229 | 230 | Here is how it is done. Simply put in 'handsontable.html' instead of 'xls' as 231 | file type. 232 | 233 | .. literalinclude:: ../../polls/views.py 234 | :lines: 153-155 235 | 236 | It is understood that you will want to embed it into your django templates. 237 | Here are the sample embedding code: 238 | 239 | .. literalinclude:: ../../polls/views.py 240 | :lines: 158-189 241 | 242 | Those views can be accessed as http://localhost:8000/polls/embedded_handson_view/ 243 | and http://localhost:8000/polls/embedded_handson_view_single/. 244 | 245 | .. image:: handsontable-embedded.png 246 | 247 | 248 | How to import one sheet instead of multi-sheet book 249 | ******************************************************************************** 250 | 251 | Previous example shows how to import a multi-sheet book. However, what exactly is 252 | needed to import only one sheet instead? Before you proceed, please empty question 253 | and choice data using django admin. 254 | 255 | Let's visit this url first http://localhost:8000/polls/imports_sheet/, where you 256 | see a similar file upload form. This time please choose 257 | `sample-sheet.xls `_ 258 | instead. Then look at django admin and see if the question data have been 259 | imported or not. 260 | 261 | Now let's look at the code: 262 | 263 | .. literalinclude:: ../../polls/views.py 264 | :lines: 104-116 265 | 266 | Because it is a single sheet, the function to call is 267 | :meth:`~django_excel.ExcelMixin.save_to_database` where you specify a model and 268 | its mapping dictionary. 269 | 270 | Have you noticed the extra parameter 'name_columns_by_row'? Why is this needed? 271 | Well, normally you *will not need* that if you have column names in the first row. 272 | In this example, the column names appears in the second row. Please open 273 | `sample-sheet.xls `_ 274 | and have a look. The straight answer is because the column names in the data 275 | appears in the 2nd row of the data matrix. 276 | 277 | .. note:: 278 | 279 | If you have imported earlier excel sheet "sample-data.xls", you will get the 280 | following warning in your console output:: 281 | 282 | Warning: Bulk insertion got below exception. Trying to do it one by one slowly. 283 | column slug is not unique <- reason 284 | One row is ignored <- action 285 | column slug is not unique 286 | What is your favourite programming language? 287 | One row is ignored 288 | column slug is not unique 289 | What is your favourite IDE? 290 | 291 | 292 | This is because question data have been imported before. Django is raising 293 | IntegrityError. For more details please read 294 | `this part of code in pyexcel-io `_, 295 | and `django-excel issue 2 `_ 296 | 297 | In order to remove those warnings, what you can do is to empty all data using 298 | django admin and redo this single sheet import again. 299 | 300 | 301 | What to do if import data overlaps existing data in the database 302 | ******************************************************************************** 303 | 304 | With new version pyexcel-io v0.1.0, you could provide the row initialization 305 | function that returns None in order to skip a row in your import data. Inside 306 | the initialization function, you could also do database update. As long as it 307 | returns None, django-excel will try to do bulk create the import data. 308 | 309 | 310 | Handle custom data export 311 | +++++++++++++++++++++++++++++++ 312 | 313 | It is also quite common to download a portion of the data in a database table, 314 | for example the result of a search query. With version 0.0.2, you can pass on a 315 | query sets to to :meth:`~django_excel.make_response_from_query_sets` and generate 316 | an excel sheet from it: 317 | 318 | .. literalinclude:: ../../polls/views.py 319 | :lines: 49, 56-65 320 | 321 | You can visit http://localhost:8000/polls/export/custom and will get the query 322 | set exported as a single sheet file as: 323 | 324 | .. image:: custom-export.png 325 | 326 | 327 | Visualize your data 328 | ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 329 | 330 | Let's go to the admin page and update some votes for the choices. 331 | 332 | .. image:: admin-vote.png 333 | 334 | In my case, I have updated all of them and have gotten something like this: 335 | 336 | .. image:: votes-handson-table.png 337 | 338 | Now, let's look at the survey result(http://localhost:8000/polls/survey_result/) 339 | for "What's your favorite IDE?": 340 | 341 | .. image:: survey-result.png 342 | 343 | `pyexcel-pygal`_ provide you the common data visualization capability to show 344 | your data intuitively. Here is the code to achieve that: 345 | 346 | .. literalinclude:: ../../polls/views.py 347 | :lines: 192-217 348 | 349 | 350 | {%endblock%} 351 | 352 | {%block api_table%} 353 | =========================== ======================================================== =================================================== 354 | data structure from file to data structures from data structures to response 355 | =========================== ======================================================== =================================================== 356 | dict :meth:`~django_excel.ExcelMixin.get_dict` :meth:`~django_excel.make_response_from_dict` 357 | records :meth:`~django_excel.ExcelMixin.get_records` :meth:`~django_excel.make_response_from_records` 358 | a list of lists :meth:`~django_excel.ExcelMixin.get_array` :meth:`~django_excel.make_response_from_array` 359 | dict of a list of lists :meth:`~django_excel.ExcelMixin.get_book_dict` :meth:`~django_excel.make_response_from_book_dict` 360 | :class:`pyexcel.Sheet` :meth:`~django_excel.ExcelMixin.get_sheet` :meth:`~django_excel.make_response` 361 | :class:`pyexcel.Book` :meth:`~django_excel.ExcelMixin.get_book` :meth:`~django_excel.make_response` 362 | database table :meth:`~django_excel.ExcelMixin.save_to_database` :meth:`~django_excel.make_response_from_a_table` 363 | :meth:`~django_excel.ExcelMixin.isave_to_database` 364 | a list of database tables :meth:`~django_excel.ExcelMixin.save_book_to_database` :meth:`~django_excel.make_response_from_tables` 365 | :meth:`~django_excel.ExcelMixin.isave_book_to_database` 366 | a database query sets :meth:`~django_excel.make_response_from_query_sets` 367 | a generator for records :meth:`~django_excel.ExcelMixin.iget_records` 368 | a generator of lists :meth:`~django_excel.ExcelMixin.iget_array` 369 | =========================== ======================================================== =================================================== 370 | {%endblock%} 371 | 372 | {%block api_doc %} 373 | **django-excel** attaches **pyexcel** functions to **InMemoryUploadedFile** and **TemporaryUploadedFile**. Hence, the following functions are available for the uploaded files, e.g. request.FILES['your_uploaded_file']. 374 | 375 | .. module:: django_excel.ExcelMixin 376 | 377 | .. method:: get_sheet(sheet_name=None, **keywords) 378 | 379 | :param sheet_name: For an excel book, there could be multiple sheets. If it is left 380 | unspecified, the sheet at index 0 is loaded. For 'csv', 'tsv' file, 381 | *sheet_name* should be None anyway. 382 | :param keywords: additional keywords to :func:`pyexcel.get_sheet` 383 | :returns: A sheet object 384 | 385 | .. method:: get_array(sheet_name=None, **keywords) 386 | 387 | :param sheet_name: same as :meth:`~django_excel.ExcelMixin.get_sheet` 388 | :param keywords: additional keywords to :func:`pyexcel.get_array` 389 | :returns: a two dimensional array, a list of lists 390 | 391 | .. method:: iget_array(sheet_name=None, **keywords) 392 | 393 | :param sheet_name: same as :meth:`~django_excel.ExcelMixin.get_sheet` 394 | :param keywords: additional keywords to :func:`pyexcel.iget_array` 395 | :returns: a generator for a two dimensional array, a list of lists 396 | 397 | .. method:: get_dict(sheet_name=None, name_columns_by_row=0, **keywords) 398 | 399 | :param sheet_name: same as :meth:`~django_excel.ExcelMixin.get_sheet` 400 | :param name_columns_by_row: uses the first row of the sheet to be column headers by default. 401 | :param keywords: additional keywords to :func:`pyexcel.get_dict` 402 | :returns: a dictionary of the file content 403 | 404 | .. method:: get_records(sheet_name=None, name_columns_by_row=0, **keywords) 405 | 406 | :param sheet_name: same as :meth:`~django_excel.ExcelMixin.get_sheet` 407 | :param name_columns_by_row: uses the first row of the sheet to be record field names by default. 408 | :param keywords: additional keywords to :func:`pyexcel.get_records` 409 | :returns: a list of dictionary of the file content 410 | 411 | .. method:: iget_records(sheet_name=None, name_columns_by_row=0, **keywords) 412 | 413 | :param sheet_name: same as :meth:`~django_excel.ExcelMixin.get_sheet` 414 | :param name_columns_by_row: uses the first row of the sheet to be record field names by default. 415 | :param keywords: additional keywords to :func:`pyexcel.iget_records` 416 | :returns: a generator for a list of dictionary of the file content 417 | 418 | .. method:: get_book(**keywords) 419 | 420 | :param keywords: additional keywords to :func:`pyexcel.get_book` 421 | :returns: a two dimensional array, a list of lists 422 | 423 | .. method:: get_book_dict(**keywords) 424 | 425 | :param keywords: additional keywords to :func:`pyexcel.get_book_dict` 426 | :returns: a two dimensional array, a list of lists 427 | 428 | .. method:: save_to_database(model=None, initializer=None, mapdict=None, **keywords) 429 | 430 | :param model: a django model 431 | :param initializer: a custom table initialization function if you have one 432 | :param mapdict: the explicit table column names if your excel data do not have the exact column names 433 | :param keywords: additional keywords to :meth:`pyexcel.Sheet.save_to_django_model` 434 | 435 | .. method:: isave_to_database(model=None, initializer=None, mapdict=None, **keywords) 436 | 437 | similar to :meth:`~django_excel.ExcelMixin.save_to_database`. But it requires 438 | less memory. 439 | 440 | This requires column names must be at the first row. 441 | 442 | .. method:: save_book_to_database(models=None, initializers=None, mapdicts=None, **keywords) 443 | 444 | :param models: a list of django models 445 | :param initializers: a list of model initialization functions. 446 | :param mapdicts: a list of explicit table column names if your excel data sheets do not have the exact column names 447 | :param keywords: additional keywords to :meth:`pyexcel.Book.save_to_django_models` 448 | 449 | .. method:: isave_book_to_database(models=None, initializers=None, mapdicts=None, **keywords) 450 | 451 | similar to :meth:`~django_excel.ExcelMixin.save_book_to_database`. But it requires 452 | less memory. 453 | 454 | This requires column names must be at the first row in each sheets. 455 | 456 | .. method:: free_resources() 457 | 458 | It should be called after iget_array and iget_records were used 459 | 460 | 461 | Response methods 462 | ----------------- 463 | 464 | .. module:: django_excel 465 | 466 | .. method:: make_response(pyexcel_instance, file_type, status=200) 467 | 468 | :param pyexcel_instance: :class:`pyexcel.Sheet` or :class:`pyexcel.Book` 469 | :param file_type: one of the following strings: 470 | 471 | * 'csv' 472 | * 'tsv' 473 | * 'csvz' 474 | * 'tsvz' 475 | * 'xls' 476 | * 'xlsx' 477 | * 'xlsm' 478 | * 'ods' 479 | 480 | :param status: unless a different status is to be returned. 481 | 482 | .. method:: make_response_from_array(array, file_type, status=200) 483 | 484 | :param array: a list of lists 485 | :param file_type: same as :meth:`~django_excel.make_response` 486 | :param status: same as :meth:`~django_excel.make_response` 487 | 488 | .. method:: make_response_from_dict(dict, file_type, status=200) 489 | 490 | :param dict: a dictionary of lists 491 | :param file_type: same as :meth:`~django_excel.make_response` 492 | :param status: same as :meth:`~django_excel.make_response` 493 | 494 | .. method:: make_response_from_records(records, file_type, status=200) 495 | 496 | :param records: a list of dictionaries 497 | :param file_type: same as :meth:`~django_excel.make_response` 498 | :param status: same as :meth:`~django_excel.make_response` 499 | 500 | 501 | .. method:: make_response_from_book_dict(book_dict, file_type, status=200) 502 | 503 | :param book_dict: a dictionary of two dimensional arrays 504 | :param file_type: same as :meth:`~django_excel.make_response` 505 | :param status: same as :meth:`~django_excel.make_response` 506 | 507 | .. method:: make_response_from_a_table(model, file_type status=200) 508 | Produce a single sheet Excel book of *file_type* 509 | 510 | :param model: a Django model 511 | :param file_type: same as :meth:`~django_excel.make_response` 512 | :param status: same as :meth:`~django_excel.make_response` 513 | 514 | .. method:: make_response_from_query_sets(query_sets, column_names, file_type status=200) 515 | 516 | Produce a single sheet Excel book of *file_type* from your custom database queries 517 | 518 | :param query_sets: a query set 519 | :param column_names: a nominated column names. It could not be None, otherwise no data is returned. 520 | :param file_type: same as :meth:`~django_excel.make_response` 521 | :param status: same as :meth:`~django_excel.make_response` 522 | 523 | .. method:: make_response_from_tables(models, file_type status=200) 524 | 525 | Produce a multiple sheet Excel book of *file_type*. It becomes the same 526 | as :meth:`~django_excel.make_response_from_a_table` if you pass *tables* 527 | with an array that has a single table 528 | 529 | :param models: a list of Django models 530 | :param file_type: same as :meth:`~django_excel.make_response` 531 | :param status: same as :meth:`~django_excel.make_response` 532 | 533 | {%endblock%} 534 | 535 | Indices and tables 536 | -------------------- 537 | 538 | * :ref:`genindex` 539 | * :ref:`modindex` 540 | * :ref:`search` 541 | -------------------------------------------------------------------------------- /.moban.d/requirements.txt: -------------------------------------------------------------------------------- 1 | {% for dependency in dependencies: %} 2 | {{dependency}} 3 | {% endfor %} 4 | -------------------------------------------------------------------------------- /.moban.d/setup.rst.jj2: -------------------------------------------------------------------------------- 1 | You will need to update your *settings.py*: 2 | 3 | .. code-block:: python 4 | 5 | FILE_UPLOAD_HANDLERS = ("django_excel.ExcelMemoryFileUploadHandler", 6 | "django_excel.TemporaryExcelFileUploadHandler") 7 | 8 | -------------------------------------------------------------------------------- /.moban.d/tests/custom_requirements.txt.jj2: -------------------------------------------------------------------------------- 1 | {% extends 'tests/requirements.txt.jj2' %} 2 | {%block extras %} 3 | lxml 4 | pyexcel-ods3>=0.1.0 5 | pyexcel-xls>=0.1.0 6 | pyexcel-xlsx>=0.1.0 7 | pyexcel-handsontable 8 | pyexcel-pygal 9 | moban 10 | black;python_version>="3.6" 11 | isort;python_version>="3.6" 12 | {%endblock%} 13 | -------------------------------------------------------------------------------- /.moban.d/verified_frameworks.rst.jj2: -------------------------------------------------------------------------------- 1 | Tested Django Versions 2 | ======================== 3 | 4 | .. image:: https://img.shields.io/badge/django-3.1.2-green.svg 5 | :target: http://travis-ci.org/pyexcel/django-excel 6 | 7 | .. image:: https://img.shields.io/badge/django-2.1-green.svg 8 | :target: http://travis-ci.org/pyexcel/django-excel 9 | 10 | .. image:: https://img.shields.io/badge/django-2.0.8-green.svg 11 | :target: http://travis-ci.org/pyexcel/django-excel 12 | 13 | .. image:: https://img.shields.io/badge/django-1.11.15-green.svg 14 | :target: http://travis-ci.org/pyexcel/django-excel 15 | 16 | .. image:: https://img.shields.io/badge/django-1.10.8-green.svg 17 | :target: http://travis-ci.org/pyexcel/django-excel 18 | 19 | .. image:: https://img.shields.io/badge/django-1.9.13-green.svg 20 | :target: http://travis-ci.org/pyexcel/django-excel 21 | 22 | .. image:: https://img.shields.io/badge/django-1.8.18-green.svg 23 | :target: http://travis-ci.org/pyexcel/django-excel 24 | -------------------------------------------------------------------------------- /.moban.yml: -------------------------------------------------------------------------------- 1 | overrides: "git://github.com/pyexcel/pyexcel-mobans!/mobanfile.yaml" 2 | configuration: 3 | configuration: django_excel.yml 4 | targets: 5 | - README.rst: custom_README.rst.jj2 6 | - setup.py: custom_setup.py.jj2 7 | - .travis.yml: custom_travis.yml.jj2 8 | - MANIFEST.in: MANIFEST.in 9 | - "docs/source/index.rst": "docs/source/django-index.rst.jj2" 10 | - "tests/requirements.txt": "tests/custom_requirements.txt.jj2" 11 | - .gitignore: gitignore.jj2 12 | -------------------------------------------------------------------------------- /.readthedocs.yml: -------------------------------------------------------------------------------- 1 | # .readthedocs.yml 2 | # Read the Docs configuration file 3 | # See https://docs.readthedocs.io/en/stable/config-file/v2.html for details 4 | 5 | # Required 6 | version: 2 7 | 8 | build: 9 | os: ubuntu-22.04 10 | tools: 11 | python: "3.12" 12 | 13 | # Build documentation in the docs/ directory with Sphinx 14 | sphinx: 15 | configuration: docs/source/conf.py 16 | 17 | # Optionally build your docs in additional formats such as PDF 18 | formats: 19 | - pdf 20 | 21 | python: 22 | install: 23 | - requirements: docs/requirements.txt 24 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | sudo: false 2 | dist: xenial 3 | language: python 4 | notifications: 5 | email: false 6 | env: 7 | - DJANGO_VERSION=1.8.18 8 | - DJANGO_VERSION=1.9.13 9 | - DJANGO_VERSION=1.10.8 10 | - DJANGO_VERSION=1.11.15 11 | - DJANGO_VERSION=2.0.8 12 | - DJANGO_VERSION=2.1 13 | - DJANGO_VERSION=3.1.2 14 | python: 15 | - 3.6 16 | - 3.7 17 | 18 | stages: 19 | - lint 20 | - test 21 | 22 | 23 | .lint: &lint 24 | git: 25 | submodules: false 26 | python: 3.6 27 | env: 28 | - MINREQ=0 29 | stage: lint 30 | script: make lint 31 | 32 | jobs: 33 | include: 34 | - *moban 35 | - *lint 36 | exclude: 37 | - python: 3.6 38 | env: DJANGO_VERSION=1.9.13 39 | - python: 3.6 40 | env: DJANGO_VERSION=1.10.8 41 | - python: 3.7 42 | env: DJANGO_VERSION=1.8.18 43 | - python: 3.7 44 | env: DJANGO_VERSION=1.9.13 45 | - python: 3.7 46 | env: DJANGO_VERSION=1.10.8 47 | - python: 3.7 48 | env: DJANGO_VERSION=1.8.18 49 | - python: 3.7 50 | env: DJANGO_VERSION=1.11.15 51 | 52 | stage: test 53 | 54 | before_install: 55 | - if [ -n "$DJANGO_VERSION" ]; then 56 | pip install Django==$DJANGO_VERSION; 57 | fi 58 | - if [[ -f min_requirements.txt && "$MINREQ" -eq 1 ]]; then 59 | mv min_requirements.txt requirements.txt ; 60 | fi 61 | - test ! -f rnd_requirements.txt || 62 | pip install --no-deps -r rnd_requirements.txt 63 | - test ! -f rnd_requirements.txt || pip install -r rnd_requirements.txt ; 64 | - pip install -r tests/requirements.txt 65 | script: 66 | - make test 67 | after_success: 68 | codecov 69 | -------------------------------------------------------------------------------- /CHANGELOG.rst: -------------------------------------------------------------------------------- 1 | Change log 2 | ================================================================================ 3 | 4 | 0.0.12 - 18.08.2022 5 | -------------------------------------------------------------------------------- 6 | 7 | **Change:** 8 | 9 | #. Support for django 2.0.x 10 | #. Tested against django 4.1 11 | #. Change xlrd version <=1.2.0 12 | 13 | 0.0.11 - tbd 14 | -------------------------------------------------------------------------------- 15 | 16 | **Updated** 17 | 18 | #. Test Support for django 3.x.x 19 | #. Tested against django 3.x.x 20 | 21 | 0.0.10 - 11.01.2018 22 | -------------------------------------------------------------------------------- 23 | 24 | **Added** 25 | 26 | #. `pyexcel-io#46 `_, 27 | expose `bulk_save` to django developers 28 | 29 | 0.0.9 - 12.07.2017 30 | -------------------------------------------------------------------------------- 31 | 32 | **Added** 33 | 34 | #. bring isave_to_database and isave_book_to_database 35 | 36 | 0.0.8 - 07.07.2017 37 | -------------------------------------------------------------------------------- 38 | 39 | **Added** 40 | 41 | #. `pyexcel#39 `_, 42 | explicitly seeking at 0 43 | 44 | 0.0.7 - 29.06.2017 45 | -------------------------------------------------------------------------------- 46 | 47 | **Added** 48 | 49 | #. `pyexcel#38 `_, 50 | Support unicode code as download file name 51 | 52 | 0.0.6 - 12.22.2016 53 | -------------------------------------------------------------------------------- 54 | 55 | **Updated** 56 | 57 | #. `pyexcel#26 `_, raise 58 | ioerror if content is empty. 59 | #. Use pyexcel-webio v0.0.9 so as to push out new version of pyexcel and its 60 | plugins. 61 | 62 | 0.0.5 - 26.08.2016 63 | -------------------------------------------------------------------------------- 64 | 65 | **Added:** 66 | 67 | #. Support for django 1.6.x 68 | #. Tested against django 1.10 69 | -------------------------------------------------------------------------------- /CONTRIBUTORS.rst: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 contributors 4 | ================================================================================ 5 | 6 | In alphabetical order: 7 | 8 | * `Minh-Long Do `_ 9 | * `oon arfiandwi `_ 10 | * `Utpal Brahma `_ 11 | * `yeop `_ 12 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2015-2022 by Onni Software Ltd. and its contributors 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms of the software as well 5 | as documentation, with or without modification, are permitted provided 6 | that the following conditions are met: 7 | 8 | * Redistributions of source code must retain the above copyright notice, this 9 | list of conditions and the following disclaimer. 10 | 11 | * Redistributions in binary form must reproduce the above copyright notice, 12 | this list of conditions and the following disclaimer in the documentation 13 | and/or other materials provided with the distribution. 14 | 15 | * Neither the name of 'django-excel' nor the names of the contributors 16 | may be used to endorse or promote products derived from this software 17 | without specific prior written permission. 18 | 19 | THIS SOFTWARE AND DOCUMENTATION IS PROVIDED BY THE COPYRIGHT HOLDERS AND 20 | CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT 21 | NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 22 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER 23 | OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 24 | EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 25 | PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 26 | PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 27 | LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 28 | NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 29 | SOFTWARE AND DOCUMENTATION, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH 30 | DAMAGE. 31 | -------------------------------------------------------------------------------- /MANIFEST.in: -------------------------------------------------------------------------------- 1 | include README.rst 2 | include LICENSE 3 | include CHANGELOG.rst 4 | recursive-include tests * 5 | recursive-include docs * 6 | prune mysite 7 | prune polls 8 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | all: test 2 | 3 | test: lint 4 | bash test.sh 5 | 6 | install_test: 7 | pip install -r tests/requirements.txt 8 | 9 | lint: 10 | bash lint.sh 11 | 12 | format: 13 | bash format.sh 14 | 15 | git-diff-check: 16 | git diff --exit-code 17 | -------------------------------------------------------------------------------- /README.rst: -------------------------------------------------------------------------------- 1 | ================================================================================ 2 | django-excel - Let you focus on data, instead of file formats 3 | ================================================================================ 4 | 5 | .. image:: https://raw.githubusercontent.com/pyexcel/pyexcel.github.io/master/images/patreon.png 6 | :target: https://www.patreon.com/chfw 7 | 8 | .. image:: https://raw.githubusercontent.com/pyexcel/pyexcel-mobans/master/images/awesome-badge.svg 9 | :target: https://awesome-python.com/#specific-formats-processing 10 | 11 | .. image:: https://codecov.io/gh/pyexcel-webwares/django-excel/branch/master/graph/badge.svg 12 | :target: https://codecov.io/gh/pyexcel-webwares/django-excel 13 | 14 | .. image:: https://badge.fury.io/py/django-excel.svg 15 | :target: https://pypi.org/project/django-excel 16 | 17 | 18 | 19 | 20 | 21 | .. image:: https://img.shields.io/gitter/room/gitterHQ/gitter.svg 22 | :target: https://gitter.im/pyexcel/Lobby 23 | 24 | .. image:: https://img.shields.io/static/v1?label=continuous%20templating&message=%E6%A8%A1%E7%89%88%E6%9B%B4%E6%96%B0&color=blue&style=flat-square 25 | :target: https://moban.readthedocs.io/en/latest/#at-scale-continous-templating-for-open-source-projects 26 | 27 | .. image:: https://img.shields.io/static/v1?label=coding%20style&message=black&color=black&style=flat-square 28 | :target: https://github.com/psf/black 29 | .. image:: https://readthedocs.org/projects/django-excel/badge/?version=latest 30 | :target: http://django-excel.readthedocs.org/en/latest/ 31 | 32 | Support the project 33 | ================================================================================ 34 | 35 | If your company has embedded pyexcel and its components into a revenue generating 36 | product, please support me on github, `patreon `_ 37 | or `bounty source `_ to maintain 38 | the project and develop it further. 39 | 40 | If you are an individual, you are welcome to support me too and for however long 41 | you feel like. As my backer, you will receive 42 | `early access to pyexcel related contents `_. 43 | 44 | And your issues will get prioritized if you would like to become my patreon as `pyexcel pro user`. 45 | 46 | With your financial support, I will be able to invest 47 | a little bit more time in coding, documentation and writing interesting posts. 48 | 49 | 50 | Known constraints 51 | ================== 52 | 53 | Fonts, colors and charts are not supported. 54 | 55 | Nor to read password protected xls, xlsx and ods files. 56 | 57 | Introduction 58 | ================================================================================ 59 | Here is a typical conversation between the developer and the user:: 60 | 61 | User: "I have uploaded an excel file" 62 | "but your application says un-supported file format" 63 | Developer: "Did you upload an xlsx file or a csv file?" 64 | User: "Well, I am not sure. I saved the data using " 65 | "Microsoft Excel. Surely, it must be in an excel format." 66 | Developer: "OK. Here is the thing. I were not told to support" 67 | "all available excel formats in day 1. Live with it" 68 | "or delay the project x number of days." 69 | 70 | **django-excel** is based on `pyexcel `_ and makes 71 | it easy to consume/produce information stored in excel files over HTTP protocol as 72 | well as on file system. This library can turn the excel data into a list of lists, 73 | a list of records(dictionaries), dictionaries of lists. And vice versa. Hence it 74 | lets you focus on data in Django based web development, instead of file formats. 75 | 76 | The idea originated from the common usability problem: when an excel file 77 | driven web application is delivered for non-developer users (ie: team assistant, 78 | human resource administrator etc). The fact is that not everyone knows (or cares) 79 | about the differences between various excel formats: csv, xls, xlsx are all 80 | the same to them. Instead of training those users about file formats, this 81 | library helps web developers to handle most of the excel file 82 | formats by providing a common programming interface. To add a specific excel 83 | file format type to you application, all you need is to install an extra pyexcel 84 | plugin. Hence no code changes to your application and no issues with excel file 85 | formats any more. Looking at the community, this library and its associated ones 86 | try to become a small and easy to install alternative to Pandas. 87 | 88 | 89 | The highlighted features are: 90 | 91 | #. excel data import into and export from databases 92 | #. turn uploaded excel file directly into Python data structure 93 | #. pass Python data structures as an excel file download 94 | #. provide data persistence as an excel file in server side 95 | #. supports csv, tsv, csvz, tsvz by default and other formats are supported via 96 | the following plugins: 97 | 98 | .. _file-format-list: 99 | .. _a-map-of-plugins-and-file-formats: 100 | 101 | .. table:: A list of file formats supported by external plugins 102 | 103 | ======================== ======================= ================= 104 | Package name Supported file formats Dependencies 105 | ======================== ======================= ================= 106 | `pyexcel-io`_ csv, csvz [#f1]_, tsv, csvz,tsvz readers depends on `chardet` 107 | tsvz [#f2]_ 108 | `pyexcel-xls`_ xls, xlsx(read only), `xlrd`_, 109 | xlsm(read only) `xlwt`_ 110 | `pyexcel-xlsx`_ xlsx `openpyxl`_ 111 | `pyexcel-ods3`_ ods `pyexcel-ezodf`_, 112 | lxml 113 | `pyexcel-ods`_ ods `odfpy`_ 114 | ======================== ======================= ================= 115 | 116 | .. table:: Dedicated file reader and writers 117 | 118 | ======================== ======================= ================= 119 | Package name Supported file formats Dependencies 120 | ======================== ======================= ================= 121 | `pyexcel-xlsxw`_ xlsx(write only) `XlsxWriter`_ 122 | `pyexcel-libxlsxw`_ xlsx(write only) `libxlsxwriter`_ 123 | `pyexcel-xlsxr`_ xlsx(read only) lxml 124 | `pyexcel-xlsbr`_ xlsb(read only) pyxlsb 125 | `pyexcel-odsr`_ read only for ods, fods lxml 126 | `pyexcel-odsw`_ write only for ods loxun 127 | `pyexcel-htmlr`_ html(read only) lxml,html5lib 128 | `pyexcel-pdfr`_ pdf(read only) camelot 129 | ======================== ======================= ================= 130 | 131 | 132 | Plugin shopping guide 133 | ------------------------ 134 | 135 | Since 2020, all pyexcel-io plugins have dropped the support for python versions 136 | which are lower than 3.6. If you want to use any of those Python versions, please use pyexcel-io 137 | and its plugins versions that are lower than 0.6.0. 138 | 139 | 140 | Except csv files, xls, xlsx and ods files are a zip of a folder containing a lot of 141 | xml files 142 | 143 | The dedicated readers for excel files can stream read 144 | 145 | 146 | In order to manage the list of plugins installed, you need to use pip to add or remove 147 | a plugin. When you use virtualenv, you can have different plugins per virtual 148 | environment. In the situation where you have multiple plugins that does the same thing 149 | in your environment, you need to tell pyexcel which plugin to use per function call. 150 | For example, pyexcel-ods and pyexcel-odsr, and you want to get_array to use pyexcel-odsr. 151 | You need to append get_array(..., library='pyexcel-odsr'). 152 | 153 | 154 | 155 | .. _pyexcel-io: https://github.com/pyexcel/pyexcel-io 156 | .. _pyexcel-xls: https://github.com/pyexcel/pyexcel-xls 157 | .. _pyexcel-xlsx: https://github.com/pyexcel/pyexcel-xlsx 158 | .. _pyexcel-ods: https://github.com/pyexcel/pyexcel-ods 159 | .. _pyexcel-ods3: https://github.com/pyexcel/pyexcel-ods3 160 | .. _pyexcel-odsr: https://github.com/pyexcel/pyexcel-odsr 161 | .. _pyexcel-odsw: https://github.com/pyexcel/pyexcel-odsw 162 | .. _pyexcel-pdfr: https://github.com/pyexcel/pyexcel-pdfr 163 | 164 | .. _pyexcel-xlsxw: https://github.com/pyexcel/pyexcel-xlsxw 165 | .. _pyexcel-libxlsxw: https://github.com/pyexcel/pyexcel-libxlsxw 166 | .. _pyexcel-xlsxr: https://github.com/pyexcel/pyexcel-xlsxr 167 | .. _pyexcel-xlsbr: https://github.com/pyexcel/pyexcel-xlsbr 168 | .. _pyexcel-htmlr: https://github.com/pyexcel/pyexcel-htmlr 169 | 170 | .. _xlrd: https://github.com/python-excel/xlrd 171 | .. _xlwt: https://github.com/python-excel/xlwt 172 | .. _openpyxl: https://bitbucket.org/openpyxl/openpyxl 173 | .. _XlsxWriter: https://github.com/jmcnamara/XlsxWriter 174 | .. _pyexcel-ezodf: https://github.com/pyexcel/pyexcel-ezodf 175 | .. _odfpy: https://github.com/eea/odfpy 176 | .. _libxlsxwriter: http://libxlsxwriter.github.io/getting_started.html 177 | 178 | .. table:: Other data renderers 179 | 180 | ======================== ======================= ================= ================== 181 | Package name Supported file formats Dependencies Python versions 182 | ======================== ======================= ================= ================== 183 | `pyexcel-text`_ write only:rst, `tabulate`_ 2.6, 2.7, 3.3, 3.4 184 | mediawiki, html, 3.5, 3.6, pypy 185 | latex, grid, pipe, 186 | orgtbl, plain simple 187 | read only: ndjson 188 | r/w: json 189 | `pyexcel-handsontable`_ handsontable in html `handsontable`_ same as above 190 | `pyexcel-pygal`_ svg chart `pygal`_ 2.7, 3.3, 3.4, 3.5 191 | 3.6, pypy 192 | `pyexcel-sortable`_ sortable table in html `csvtotable`_ same as above 193 | `pyexcel-gantt`_ gantt chart in html `frappe-gantt`_ except pypy, same 194 | as above 195 | ======================== ======================= ================= ================== 196 | 197 | .. _pyexcel-text: https://github.com/pyexcel/pyexcel-text 198 | .. _tabulate: https://bitbucket.org/astanin/python-tabulate 199 | .. _pyexcel-handsontable: https://github.com/pyexcel/pyexcel-handsontable 200 | .. _handsontable: https://cdnjs.com/libraries/handsontable 201 | .. _pyexcel-pygal: https://github.com/pyexcel/pyexcel-chart 202 | .. _pygal: https://github.com/Kozea/pygal 203 | .. _pyexcel-matplotlib: https://github.com/pyexcel/pyexcel-matplotlib 204 | .. _matplotlib: https://matplotlib.org 205 | .. _pyexcel-sortable: https://github.com/pyexcel/pyexcel-sortable 206 | .. _csvtotable: https://github.com/vividvilla/csvtotable 207 | .. _pyexcel-gantt: https://github.com/pyexcel/pyexcel-gantt 208 | .. _frappe-gantt: https://github.com/frappe/gantt 209 | 210 | .. rubric:: Footnotes 211 | 212 | .. [#f1] zipped csv file 213 | .. [#f2] zipped tsv file 214 | 215 | 216 | This library makes information processing involving various excel files as easy as 217 | processing array, dictionary when processing file upload/download, data import into 218 | and export from SQL databases, information analysis and persistence. It uses 219 | **pyexcel** and its plugins: 220 | 221 | #. to provide one uniform programming interface to handle csv, tsv, xls, xlsx, xlsm and ods formats. 222 | #. to provide one-stop utility to import the data in uploaded file into a database and to export tables in a database as excel files for file download. 223 | #. to provide the same interface for information persistence at server side: saving a uploaded excel file to and loading a saved excel file from file system. 224 | 225 | 226 | 227 | Tested Django Versions 228 | ======================== 229 | 230 | .. image:: https://img.shields.io/badge/django-3.1.2-green.svg 231 | :target: http://travis-ci.org/pyexcel/django-excel 232 | 233 | .. image:: https://img.shields.io/badge/django-2.1-green.svg 234 | :target: http://travis-ci.org/pyexcel/django-excel 235 | 236 | .. image:: https://img.shields.io/badge/django-2.0.8-green.svg 237 | :target: http://travis-ci.org/pyexcel/django-excel 238 | 239 | .. image:: https://img.shields.io/badge/django-1.11.15-green.svg 240 | :target: http://travis-ci.org/pyexcel/django-excel 241 | 242 | .. image:: https://img.shields.io/badge/django-1.10.8-green.svg 243 | :target: http://travis-ci.org/pyexcel/django-excel 244 | 245 | .. image:: https://img.shields.io/badge/django-1.9.13-green.svg 246 | :target: http://travis-ci.org/pyexcel/django-excel 247 | 248 | .. image:: https://img.shields.io/badge/django-1.8.18-green.svg 249 | :target: http://travis-ci.org/pyexcel/django-excel 250 | 251 | Installation 252 | ================================================================================ 253 | 254 | You can install django-excel via pip: 255 | 256 | .. code-block:: bash 257 | 258 | $ pip install django-excel 259 | 260 | 261 | or clone it and install it: 262 | 263 | .. code-block:: bash 264 | 265 | $ git clone https://github.com/pyexcel-webwares/django-excel.git 266 | $ cd django-excel 267 | $ python setup.py install 268 | 269 | Setup 270 | ====== 271 | 272 | You will need to update your *settings.py*: 273 | 274 | .. code-block:: python 275 | 276 | FILE_UPLOAD_HANDLERS = ("django_excel.ExcelMemoryFileUploadHandler", 277 | "django_excel.TemporaryExcelFileUploadHandler") 278 | 279 | 280 | 281 | Usage 282 | ========= 283 | Here is the example viewing function codes: 284 | 285 | .. code-block:: python 286 | 287 | from django.shortcuts import render_to_response 288 | from django.http import HttpResponseBadRequest 289 | from django import forms 290 | from django.template import RequestContext 291 | import django_excel as excel 292 | 293 | class UploadFileForm(forms.Form): 294 | file = forms.FileField() 295 | 296 | def upload(request): 297 | if request.method == "POST": 298 | form = UploadFileForm(request.POST, request.FILES) 299 | if form.is_valid(): 300 | filehandle = request.FILES['file'] 301 | return excel.make_response(filehandle.get_sheet(), "csv") 302 | else: 303 | return HttpResponseBadRequest() 304 | else: 305 | form = UploadFileForm() 306 | return render_to_response('upload_form.html', 307 | {'form': form}, 308 | context_instance=RequestContext(request)) 309 | 310 | def download(request): 311 | sheet = excel.pe.Sheet([[1, 2],[3, 4]]) 312 | return excel.make_response(sheet, "csv") 313 | 314 | Development guide 315 | ================================================================================ 316 | 317 | Development steps for code changes 318 | 319 | #. git clone https://github.com/pyexcel/django-excel.git 320 | #. cd django-excel 321 | 322 | Upgrade your setup tools and pip. They are needed for development and testing only: 323 | 324 | #. pip install --upgrade setuptools pip 325 | 326 | Then install relevant development requirements: 327 | 328 | #. pip install -r rnd_requirements.txt # if such a file exists 329 | #. pip install -r requirements.txt 330 | #. pip install -r tests/requirements.txt 331 | 332 | Once you have finished your changes, please provide test case(s), relevant documentation 333 | and update changelog.yml 334 | 335 | .. note:: 336 | 337 | As to rnd_requirements.txt, usually, it is created when a dependent 338 | library is not released. Once the dependecy is installed 339 | (will be released), the future 340 | version of the dependency in the requirements.txt will be valid. 341 | 342 | 343 | How to test your contribution 344 | ------------------------------ 345 | 346 | Although `nose` and `doctest` are both used in code testing, it is adviable that unit tests are put in tests. `doctest` is incorporated only to make sure the code examples in documentation remain valid across different development releases. 347 | 348 | On Linux/Unix systems, please launch your tests like this:: 349 | 350 | $ make 351 | 352 | On Windows, please issue this command:: 353 | 354 | > test.bat 355 | 356 | 357 | Before you commit 358 | ------------------------------ 359 | 360 | Please run:: 361 | 362 | $ make format 363 | 364 | so as to beautify your code otherwise your build may fail your unit test. 365 | 366 | 367 | 368 | 369 | License 370 | ================================================================================ 371 | 372 | New BSD License 373 | -------------------------------------------------------------------------------- /_compact.py: -------------------------------------------------------------------------------- 1 | try: 2 | from django.http import JsonResponse 3 | except ImportError: 4 | from django.http import HttpResponse 5 | import json 6 | 7 | def JsonResponse(data): 8 | return HttpResponse(json.dumps(data), 9 | content_type="application/json") 10 | -------------------------------------------------------------------------------- /changelog.yml: -------------------------------------------------------------------------------- 1 | name: django-excel 2 | organisation: pyexcel-webwares 3 | releases: 4 | - changes: 5 | - action: 'Change:' 6 | details: 7 | - Support for django 2.0.x 8 | - Tested against django 4.1 9 | - Change xlrd version <=1.2.0 10 | date: 18.08.2022 11 | version: 0.0.12 12 | - changes: 13 | - action: Updated 14 | details: 15 | - 'Test Support for django 3.x.x' 16 | - 'Tested against django 3.x.x' 17 | date: tbd 18 | version: 0.0.11 19 | - changes: 20 | - action: Added 21 | details: 22 | - '`pyexcel-io#46`, expose `bulk_save` to django developers' 23 | date: 11.01.2018 24 | version: 0.0.10 25 | - changes: 26 | - action: Added 27 | details: 28 | - bring isave_to_database and isave_book_to_database 29 | date: 12.07.2017 30 | version: 0.0.9 31 | - changes: 32 | - action: Added 33 | details: 34 | - '`pyexcel#39`, explicitly seeking at 0' 35 | date: 07.07.2017 36 | version: 0.0.8 37 | - changes: 38 | - action: Added 39 | details: 40 | - '`pyexcel#38`, Support unicode code as download file name' 41 | date: 29.06.2017 42 | version: 0.0.7 43 | - changes: 44 | - action: Updated 45 | details: 46 | - '`pyexcel#26`, raise ioerror if content is empty.' 47 | - Use pyexcel-webio v0.0.9 so as to push out new version of pyexcel and its plugins. 48 | date: 12.22.2016 49 | version: 0.0.6 50 | - changes: 51 | - action: 'Added:' 52 | details: 53 | - Support for django 1.6.x 54 | - Tested against django 1.10 55 | date: 26.08.2016 56 | version: 0.0.5 57 | -------------------------------------------------------------------------------- /db.sqlite3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pyexcel-webwares/django-excel/a17fea39ffcbf3b7c0d195cab5002ba8c8d416a9/db.sqlite3 -------------------------------------------------------------------------------- /django_excel.yml: -------------------------------------------------------------------------------- 1 | overrides: "pyexcel.yaml" 2 | name: "django-excel" 3 | organisation: pyexcel-webwares 4 | version: 0.0.10 5 | current_version: 0.0.10 6 | release: 0.0.10 7 | webframework: Django 8 | dependencies: 9 | - pyexcel>=0.5.7 10 | - pyexcel-webio>=0.1.2 11 | - Django>=1.6.1 12 | extra_dependencies: 13 | - xls: 14 | - pyexcel-xls>=0.4.0 15 | - xlsx: 16 | - pyexcel-xlsx>=0.4.0 17 | - ods: 18 | - pyexcel-ods3>=0.4.0 19 | test_dependencies: 20 | - lxml 21 | - pyexcel-ods3>=0.1.0 22 | - pyexcel-xls>=0.1.0 23 | - pyexcel-xlsx>=0.1.0 24 | - pyexcel-handsontable 25 | - pyexcel-pygal 26 | keywords: 27 | - API 28 | - Django 29 | description: 30 | A django middleware that provides one application programming interface to read and write data in different excel file formats 31 | flake8_options: --ignore=F401,E402,E501,W503 32 | moban_command: false 33 | -------------------------------------------------------------------------------- /django_excel/__init__.py: -------------------------------------------------------------------------------- 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 | import pyexcel as pe 12 | import pyexcel_webio as webio 13 | from django.core.files.uploadedfile import ( 14 | InMemoryUploadedFile, 15 | TemporaryUploadedFile, 16 | ) 17 | from django.core.files.uploadhandler import ( 18 | MemoryFileUploadHandler, 19 | TemporaryFileUploadHandler, 20 | ) 21 | from django.http import HttpResponse 22 | 23 | from ._compact import DJANGO_ONE_SIX, PY2_VERSION, urllib_quote 24 | 25 | 26 | class ExcelMixin(webio.ExcelInput): 27 | """ 28 | Provide additional pyexcel-webio methods to Django's UploadedFiles 29 | """ 30 | 31 | def get_params(self, **keywords): 32 | extension = self.name.split(".")[-1] 33 | keywords["file_type"] = extension 34 | self.file.seek(0) 35 | content = self.file.read() 36 | if content: 37 | keywords["file_content"] = content 38 | else: 39 | raise IOError("No content was uploaded.") 40 | return keywords 41 | 42 | def save_to_database( 43 | self, model=None, initializer=None, mapdict=None, **keywords 44 | ): 45 | """ 46 | Save data from a sheet to a nominated django model 47 | """ 48 | params = self.get_params(**keywords) 49 | if "name_columns_by_row" not in params: 50 | params["name_columns_by_row"] = 0 51 | if "name_rows_by_column" not in params: 52 | params["name_rows_by_column"] = -1 53 | params["dest_model"] = model 54 | params["dest_initializer"] = initializer 55 | params["dest_mapdict"] = mapdict 56 | pe.save_as(**params) 57 | 58 | def save_book_to_database( 59 | self, 60 | models=None, 61 | initializers=None, 62 | mapdicts=None, 63 | batch_size=None, 64 | bulk_save=None, 65 | **keywords 66 | ): 67 | """ 68 | Save data from a book to a nominated django models 69 | """ 70 | params = self.get_params(**keywords) 71 | params["dest_models"] = models 72 | params["dest_initializers"] = initializers 73 | params["dest_mapdicts"] = mapdicts 74 | params["dest_batch_size"] = batch_size 75 | params["dest_bulk_save"] = bulk_save 76 | pe.save_book_as(**params) 77 | 78 | def isave_to_database( 79 | self, model=None, initializer=None, mapdict=None, **keywords 80 | ): 81 | """ 82 | Save data from a sheet to a nominated django model 83 | """ 84 | params = self.get_params(**keywords) 85 | params["dest_model"] = model 86 | params["dest_initializer"] = initializer 87 | params["dest_mapdict"] = mapdict 88 | pe.isave_as(**params) 89 | self.free_resources() 90 | 91 | def isave_book_to_database( 92 | self, 93 | models=None, 94 | initializers=None, 95 | mapdicts=None, 96 | batch_size=None, 97 | bulk_save=None, 98 | **keywords 99 | ): 100 | """ 101 | Save data from a book to a nominated django models 102 | """ 103 | params = self.get_params(**keywords) 104 | params["dest_models"] = models 105 | params["dest_initializers"] = initializers 106 | params["dest_mapdicts"] = mapdicts 107 | params["dest_batch_size"] = batch_size 108 | params["dest_bulk_save"] = bulk_save 109 | pe.isave_book_as(**params) 110 | 111 | 112 | class ExcelInMemoryUploadedFile(ExcelMixin, InMemoryUploadedFile): 113 | """ 114 | Mix-in pyexcel-webio methods in InMemoryUploadedFile 115 | """ 116 | 117 | pass 118 | 119 | 120 | class TemporaryUploadedExcelFile(ExcelMixin, TemporaryUploadedFile): 121 | """ 122 | Mix-in pyexcel-webio methods in TemporaryUploadedFile 123 | """ 124 | 125 | pass 126 | 127 | 128 | class ExcelMemoryFileUploadHandler(MemoryFileUploadHandler): 129 | """ 130 | Override MemoryFileUploadHandler to bring in ExcelInMemoryUploadedFile 131 | """ 132 | 133 | def file_complete(self, file_size): 134 | if not self.activated: 135 | return 136 | self.file.seek(0) 137 | keywords = dict( 138 | file=self.file, 139 | field_name=self.field_name, 140 | name=self.file_name, 141 | content_type=self.content_type, 142 | size=file_size, 143 | charset=self.charset, 144 | ) 145 | if not DJANGO_ONE_SIX: 146 | keywords["content_type_extra"] = self.content_type_extra 147 | return ExcelInMemoryUploadedFile(**keywords) 148 | 149 | 150 | class TemporaryExcelFileUploadHandler(TemporaryFileUploadHandler): 151 | """ 152 | Override TemporaryFileUploadHandler to bring in TemporaryUploadedExcelFile 153 | """ 154 | 155 | def new_file(self, file_name, *args, **kwargs): 156 | """ 157 | Create the file object to append to as data is coming in. 158 | """ 159 | super(TemporaryFileUploadHandler, self).new_file( 160 | file_name, *args, **kwargs 161 | ) 162 | custom_args = [self.file_name, self.content_type, 0, self.charset] 163 | if not DJANGO_ONE_SIX: 164 | custom_args.append(self.content_type_extra) 165 | self.file = TemporaryUploadedExcelFile(*custom_args) 166 | 167 | 168 | def _make_response(content, content_type, status, file_name=None): 169 | """ 170 | Custom response function that is called by pyexcel-webio 171 | """ 172 | response = HttpResponse(content, content_type=content_type, status=status) 173 | if file_name: 174 | if PY2_VERSION and isinstance(file_name, unicode): 175 | file_name = file_name.encode("utf-8") 176 | url_encoded_file_name = urllib_quote(file_name) 177 | response[ 178 | "Content-Disposition" 179 | ] = "attachment; filename=%s;filename*=utf-8''%s" % ( 180 | url_encoded_file_name, 181 | url_encoded_file_name, 182 | ) 183 | return response 184 | 185 | 186 | webio.init_webio(_make_response) 187 | 188 | 189 | from pyexcel_webio import ( 190 | make_response, 191 | make_response_from_array, # noqa 192 | make_response_from_book_dict, 193 | make_response_from_dict, 194 | make_response_from_query_sets, 195 | make_response_from_records, 196 | ) 197 | 198 | 199 | def make_response_from_a_table( 200 | model, file_type, status=200, file_name=None, **keywords 201 | ): 202 | """ 203 | Produce a single sheet Excel book of *file_type* 204 | 205 | :param model: a Django model 206 | :param file_type: same as :meth:`~django_excel.make_response` 207 | :param status: same as :meth:`~django_excel.make_response` 208 | """ 209 | sheet = pe.get_sheet(model=model, **keywords) 210 | return make_response( 211 | sheet, file_type, status, file_name=file_name, **keywords 212 | ) 213 | 214 | 215 | def make_response_from_tables( 216 | models, file_type, status=200, file_name=None, **keywords 217 | ): 218 | """ 219 | Produce a multiple sheet Excel book of *file_type*. It becomes the same 220 | as :meth:`~django_excel.make_response_from_a_table` if you pass *tables* 221 | with an array that has a single table 222 | 223 | :param models: a list of Django models 224 | :param file_type: same as :meth:`~django_excel.make_response` 225 | :param status: same as :meth:`~django_excel.make_response` 226 | """ 227 | book = pe.get_book(models=models, **keywords) 228 | return make_response( 229 | book, file_type, status, file_name=file_name, **keywords 230 | ) 231 | -------------------------------------------------------------------------------- /django_excel/_compact.py: -------------------------------------------------------------------------------- 1 | from distutils.version import LooseVersion 2 | 3 | import pkg_resources 4 | 5 | try: 6 | # if in py2 7 | from urllib import quote as urllib_quote 8 | 9 | PY2_VERSION = True 10 | except ImportError: 11 | # else (aka in py3) 12 | from urllib.parse import quote as urllib_quote # noqa: F401 13 | 14 | PY2_VERSION = False 15 | 16 | 17 | django_version = pkg_resources.get_distribution("django").version 18 | 19 | 20 | DJANGO_ONE_SIX = LooseVersion(django_version) < LooseVersion( 21 | "1.7.0" 22 | ) and LooseVersion(django_version) > LooseVersion("1.5.12") 23 | -------------------------------------------------------------------------------- /docs/Makefile: -------------------------------------------------------------------------------- 1 | # Makefile for Sphinx documentation 2 | # 3 | 4 | # You can set these variables from the command line. 5 | SPHINXOPTS = 6 | SPHINXBUILD = sphinx-build 7 | PAPER = 8 | BUILDDIR = build 9 | 10 | # User-friendly check for sphinx-build 11 | ifeq ($(shell which $(SPHINXBUILD) >/dev/null 2>&1; echo $$?), 1) 12 | $(error The '$(SPHINXBUILD)' command was not found. Make sure you have Sphinx installed, then set the SPHINXBUILD environment variable to point to the full path of the '$(SPHINXBUILD)' executable. Alternatively you can add the directory with the executable to your PATH. If you don't have Sphinx installed, grab it from http://sphinx-doc.org/) 13 | endif 14 | 15 | # Internal variables. 16 | PAPEROPT_a4 = -D latex_paper_size=a4 17 | PAPEROPT_letter = -D latex_paper_size=letter 18 | ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) source 19 | # the i18n builder cannot share the environment and doctrees with the others 20 | I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) source 21 | 22 | .PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest gettext 23 | 24 | help: 25 | @echo "Please use \`make ' where is one of" 26 | @echo " html to make standalone HTML files" 27 | @echo " dirhtml to make HTML files named index.html in directories" 28 | @echo " singlehtml to make a single large HTML file" 29 | @echo " pickle to make pickle files" 30 | @echo " json to make JSON files" 31 | @echo " htmlhelp to make HTML files and a HTML help project" 32 | @echo " qthelp to make HTML files and a qthelp project" 33 | @echo " devhelp to make HTML files and a Devhelp project" 34 | @echo " epub to make an epub" 35 | @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter" 36 | @echo " latexpdf to make LaTeX files and run them through pdflatex" 37 | @echo " latexpdfja to make LaTeX files and run them through platex/dvipdfmx" 38 | @echo " text to make text files" 39 | @echo " man to make manual pages" 40 | @echo " texinfo to make Texinfo files" 41 | @echo " info to make Texinfo files and run them through makeinfo" 42 | @echo " gettext to make PO message catalogs" 43 | @echo " changes to make an overview of all changed/added/deprecated items" 44 | @echo " xml to make Docutils-native XML files" 45 | @echo " pseudoxml to make pseudoxml-XML files for display purposes" 46 | @echo " linkcheck to check all external links for integrity" 47 | @echo " doctest to run all doctests embedded in the documentation (if enabled)" 48 | 49 | clean: 50 | rm -rf $(BUILDDIR)/* 51 | 52 | html: 53 | $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html 54 | @echo 55 | @echo "Build finished. The HTML pages are in $(BUILDDIR)/html." 56 | 57 | dirhtml: 58 | $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml 59 | @echo 60 | @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml." 61 | 62 | singlehtml: 63 | $(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml 64 | @echo 65 | @echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml." 66 | 67 | pickle: 68 | $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle 69 | @echo 70 | @echo "Build finished; now you can process the pickle files." 71 | 72 | json: 73 | $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json 74 | @echo 75 | @echo "Build finished; now you can process the JSON files." 76 | 77 | htmlhelp: 78 | $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp 79 | @echo 80 | @echo "Build finished; now you can run HTML Help Workshop with the" \ 81 | ".hhp project file in $(BUILDDIR)/htmlhelp." 82 | 83 | qthelp: 84 | $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp 85 | @echo 86 | @echo "Build finished; now you can run "qcollectiongenerator" with the" \ 87 | ".qhcp project file in $(BUILDDIR)/qthelp, like this:" 88 | @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/django-excel.qhcp" 89 | @echo "To view the help file:" 90 | @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/django-excel.qhc" 91 | 92 | devhelp: 93 | $(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp 94 | @echo 95 | @echo "Build finished." 96 | @echo "To view the help file:" 97 | @echo "# mkdir -p $$HOME/.local/share/devhelp/django-excel" 98 | @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/django-excel" 99 | @echo "# devhelp" 100 | 101 | epub: 102 | $(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub 103 | @echo 104 | @echo "Build finished. The epub file is in $(BUILDDIR)/epub." 105 | 106 | latex: 107 | $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex 108 | @echo 109 | @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex." 110 | @echo "Run \`make' in that directory to run these through (pdf)latex" \ 111 | "(use \`make latexpdf' here to do that automatically)." 112 | 113 | latexpdf: 114 | $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex 115 | @echo "Running LaTeX files through pdflatex..." 116 | $(MAKE) -C $(BUILDDIR)/latex all-pdf 117 | @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." 118 | 119 | latexpdfja: 120 | $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex 121 | @echo "Running LaTeX files through platex and dvipdfmx..." 122 | $(MAKE) -C $(BUILDDIR)/latex all-pdf-ja 123 | @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." 124 | 125 | text: 126 | $(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text 127 | @echo 128 | @echo "Build finished. The text files are in $(BUILDDIR)/text." 129 | 130 | man: 131 | $(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man 132 | @echo 133 | @echo "Build finished. The manual pages are in $(BUILDDIR)/man." 134 | 135 | texinfo: 136 | $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo 137 | @echo 138 | @echo "Build finished. The Texinfo files are in $(BUILDDIR)/texinfo." 139 | @echo "Run \`make' in that directory to run these through makeinfo" \ 140 | "(use \`make info' here to do that automatically)." 141 | 142 | info: 143 | $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo 144 | @echo "Running Texinfo files through makeinfo..." 145 | make -C $(BUILDDIR)/texinfo info 146 | @echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo." 147 | 148 | gettext: 149 | $(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale 150 | @echo 151 | @echo "Build finished. The message catalogs are in $(BUILDDIR)/locale." 152 | 153 | changes: 154 | $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes 155 | @echo 156 | @echo "The overview file is in $(BUILDDIR)/changes." 157 | 158 | linkcheck: 159 | $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck 160 | @echo 161 | @echo "Link check complete; look for any errors in the above output " \ 162 | "or in $(BUILDDIR)/linkcheck/output.txt." 163 | 164 | doctest: 165 | $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest 166 | @echo "Testing of doctests in the sources finished, look at the " \ 167 | "results in $(BUILDDIR)/doctest/output.txt." 168 | 169 | xml: 170 | $(SPHINXBUILD) -b xml $(ALLSPHINXOPTS) $(BUILDDIR)/xml 171 | @echo 172 | @echo "Build finished. The XML files are in $(BUILDDIR)/xml." 173 | 174 | pseudoxml: 175 | $(SPHINXBUILD) -b pseudoxml $(ALLSPHINXOPTS) $(BUILDDIR)/pseudoxml 176 | @echo 177 | @echo "Build finished. The pseudo-XML files are in $(BUILDDIR)/pseudoxml." 178 | -------------------------------------------------------------------------------- /docs/make.bat: -------------------------------------------------------------------------------- 1 | @ECHO OFF 2 | 3 | REM Command file for Sphinx documentation 4 | 5 | if "%SPHINXBUILD%" == "" ( 6 | set SPHINXBUILD=sphinx-build 7 | ) 8 | set BUILDDIR=build 9 | set ALLSPHINXOPTS=-d %BUILDDIR%/doctrees %SPHINXOPTS% source 10 | set I18NSPHINXOPTS=%SPHINXOPTS% source 11 | if NOT "%PAPER%" == "" ( 12 | set ALLSPHINXOPTS=-D latex_paper_size=%PAPER% %ALLSPHINXOPTS% 13 | set I18NSPHINXOPTS=-D latex_paper_size=%PAPER% %I18NSPHINXOPTS% 14 | ) 15 | 16 | if "%1" == "" goto help 17 | 18 | if "%1" == "help" ( 19 | :help 20 | echo.Please use `make ^` where ^ is one of 21 | echo. html to make standalone HTML files 22 | echo. dirhtml to make HTML files named index.html in directories 23 | echo. singlehtml to make a single large HTML file 24 | echo. pickle to make pickle files 25 | echo. json to make JSON files 26 | echo. htmlhelp to make HTML files and a HTML help project 27 | echo. qthelp to make HTML files and a qthelp project 28 | echo. devhelp to make HTML files and a Devhelp project 29 | echo. epub to make an epub 30 | echo. latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter 31 | echo. text to make text files 32 | echo. man to make manual pages 33 | echo. texinfo to make Texinfo files 34 | echo. gettext to make PO message catalogs 35 | echo. changes to make an overview over all changed/added/deprecated items 36 | echo. xml to make Docutils-native XML files 37 | echo. pseudoxml to make pseudoxml-XML files for display purposes 38 | echo. linkcheck to check all external links for integrity 39 | echo. doctest to run all doctests embedded in the documentation if enabled 40 | goto end 41 | ) 42 | 43 | if "%1" == "clean" ( 44 | for /d %%i in (%BUILDDIR%\*) do rmdir /q /s %%i 45 | del /q /s %BUILDDIR%\* 46 | goto end 47 | ) 48 | 49 | 50 | %SPHINXBUILD% 2> nul 51 | if errorlevel 9009 ( 52 | echo. 53 | echo.The 'sphinx-build' command was not found. Make sure you have Sphinx 54 | echo.installed, then set the SPHINXBUILD environment variable to point 55 | echo.to the full path of the 'sphinx-build' executable. Alternatively you 56 | echo.may add the Sphinx directory to PATH. 57 | echo. 58 | echo.If you don't have Sphinx installed, grab it from 59 | echo.http://sphinx-doc.org/ 60 | exit /b 1 61 | ) 62 | 63 | if "%1" == "html" ( 64 | %SPHINXBUILD% -b html %ALLSPHINXOPTS% %BUILDDIR%/html 65 | if errorlevel 1 exit /b 1 66 | echo. 67 | echo.Build finished. The HTML pages are in %BUILDDIR%/html. 68 | goto end 69 | ) 70 | 71 | if "%1" == "dirhtml" ( 72 | %SPHINXBUILD% -b dirhtml %ALLSPHINXOPTS% %BUILDDIR%/dirhtml 73 | if errorlevel 1 exit /b 1 74 | echo. 75 | echo.Build finished. The HTML pages are in %BUILDDIR%/dirhtml. 76 | goto end 77 | ) 78 | 79 | if "%1" == "singlehtml" ( 80 | %SPHINXBUILD% -b singlehtml %ALLSPHINXOPTS% %BUILDDIR%/singlehtml 81 | if errorlevel 1 exit /b 1 82 | echo. 83 | echo.Build finished. The HTML pages are in %BUILDDIR%/singlehtml. 84 | goto end 85 | ) 86 | 87 | if "%1" == "pickle" ( 88 | %SPHINXBUILD% -b pickle %ALLSPHINXOPTS% %BUILDDIR%/pickle 89 | if errorlevel 1 exit /b 1 90 | echo. 91 | echo.Build finished; now you can process the pickle files. 92 | goto end 93 | ) 94 | 95 | if "%1" == "json" ( 96 | %SPHINXBUILD% -b json %ALLSPHINXOPTS% %BUILDDIR%/json 97 | if errorlevel 1 exit /b 1 98 | echo. 99 | echo.Build finished; now you can process the JSON files. 100 | goto end 101 | ) 102 | 103 | if "%1" == "htmlhelp" ( 104 | %SPHINXBUILD% -b htmlhelp %ALLSPHINXOPTS% %BUILDDIR%/htmlhelp 105 | if errorlevel 1 exit /b 1 106 | echo. 107 | echo.Build finished; now you can run HTML Help Workshop with the ^ 108 | .hhp project file in %BUILDDIR%/htmlhelp. 109 | goto end 110 | ) 111 | 112 | if "%1" == "qthelp" ( 113 | %SPHINXBUILD% -b qthelp %ALLSPHINXOPTS% %BUILDDIR%/qthelp 114 | if errorlevel 1 exit /b 1 115 | echo. 116 | echo.Build finished; now you can run "qcollectiongenerator" with the ^ 117 | .qhcp project file in %BUILDDIR%/qthelp, like this: 118 | echo.^> qcollectiongenerator %BUILDDIR%\qthelp\django-excel.qhcp 119 | echo.To view the help file: 120 | echo.^> assistant -collectionFile %BUILDDIR%\qthelp\django-excel.ghc 121 | goto end 122 | ) 123 | 124 | if "%1" == "devhelp" ( 125 | %SPHINXBUILD% -b devhelp %ALLSPHINXOPTS% %BUILDDIR%/devhelp 126 | if errorlevel 1 exit /b 1 127 | echo. 128 | echo.Build finished. 129 | goto end 130 | ) 131 | 132 | if "%1" == "epub" ( 133 | %SPHINXBUILD% -b epub %ALLSPHINXOPTS% %BUILDDIR%/epub 134 | if errorlevel 1 exit /b 1 135 | echo. 136 | echo.Build finished. The epub file is in %BUILDDIR%/epub. 137 | goto end 138 | ) 139 | 140 | if "%1" == "latex" ( 141 | %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex 142 | if errorlevel 1 exit /b 1 143 | echo. 144 | echo.Build finished; the LaTeX files are in %BUILDDIR%/latex. 145 | goto end 146 | ) 147 | 148 | if "%1" == "latexpdf" ( 149 | %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex 150 | cd %BUILDDIR%/latex 151 | make all-pdf 152 | cd %BUILDDIR%/.. 153 | echo. 154 | echo.Build finished; the PDF files are in %BUILDDIR%/latex. 155 | goto end 156 | ) 157 | 158 | if "%1" == "latexpdfja" ( 159 | %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex 160 | cd %BUILDDIR%/latex 161 | make all-pdf-ja 162 | cd %BUILDDIR%/.. 163 | echo. 164 | echo.Build finished; the PDF files are in %BUILDDIR%/latex. 165 | goto end 166 | ) 167 | 168 | if "%1" == "text" ( 169 | %SPHINXBUILD% -b text %ALLSPHINXOPTS% %BUILDDIR%/text 170 | if errorlevel 1 exit /b 1 171 | echo. 172 | echo.Build finished. The text files are in %BUILDDIR%/text. 173 | goto end 174 | ) 175 | 176 | if "%1" == "man" ( 177 | %SPHINXBUILD% -b man %ALLSPHINXOPTS% %BUILDDIR%/man 178 | if errorlevel 1 exit /b 1 179 | echo. 180 | echo.Build finished. The manual pages are in %BUILDDIR%/man. 181 | goto end 182 | ) 183 | 184 | if "%1" == "texinfo" ( 185 | %SPHINXBUILD% -b texinfo %ALLSPHINXOPTS% %BUILDDIR%/texinfo 186 | if errorlevel 1 exit /b 1 187 | echo. 188 | echo.Build finished. The Texinfo files are in %BUILDDIR%/texinfo. 189 | goto end 190 | ) 191 | 192 | if "%1" == "gettext" ( 193 | %SPHINXBUILD% -b gettext %I18NSPHINXOPTS% %BUILDDIR%/locale 194 | if errorlevel 1 exit /b 1 195 | echo. 196 | echo.Build finished. The message catalogs are in %BUILDDIR%/locale. 197 | goto end 198 | ) 199 | 200 | if "%1" == "changes" ( 201 | %SPHINXBUILD% -b changes %ALLSPHINXOPTS% %BUILDDIR%/changes 202 | if errorlevel 1 exit /b 1 203 | echo. 204 | echo.The overview file is in %BUILDDIR%/changes. 205 | goto end 206 | ) 207 | 208 | if "%1" == "linkcheck" ( 209 | %SPHINXBUILD% -b linkcheck %ALLSPHINXOPTS% %BUILDDIR%/linkcheck 210 | if errorlevel 1 exit /b 1 211 | echo. 212 | echo.Link check complete; look for any errors in the above output ^ 213 | or in %BUILDDIR%/linkcheck/output.txt. 214 | goto end 215 | ) 216 | 217 | if "%1" == "doctest" ( 218 | %SPHINXBUILD% -b doctest %ALLSPHINXOPTS% %BUILDDIR%/doctest 219 | if errorlevel 1 exit /b 1 220 | echo. 221 | echo.Testing of doctests in the sources finished, look at the ^ 222 | results in %BUILDDIR%/doctest/output.txt. 223 | goto end 224 | ) 225 | 226 | if "%1" == "xml" ( 227 | %SPHINXBUILD% -b xml %ALLSPHINXOPTS% %BUILDDIR%/xml 228 | if errorlevel 1 exit /b 1 229 | echo. 230 | echo.Build finished. The XML files are in %BUILDDIR%/xml. 231 | goto end 232 | ) 233 | 234 | if "%1" == "pseudoxml" ( 235 | %SPHINXBUILD% -b pseudoxml %ALLSPHINXOPTS% %BUILDDIR%/pseudoxml 236 | if errorlevel 1 exit /b 1 237 | echo. 238 | echo.Build finished. The pseudo-XML files are in %BUILDDIR%/pseudoxml. 239 | goto end 240 | ) 241 | 242 | :end 243 | -------------------------------------------------------------------------------- /docs/requirements.txt: -------------------------------------------------------------------------------- 1 | sphinxcontrib-excel 2 | 3 | 4 | -------------------------------------------------------------------------------- /docs/source/_static/handsontable.full.min.css: -------------------------------------------------------------------------------- 1 | @charset "UTF-8";/*! 2 | (The MIT License) 3 | 4 | Copyright (c) 2012-2014 Marcin Warpechowski 5 | Copyright (c) 2015 Handsoncode sp. z o.o. 6 | 7 | Permission is hereby granted, free of charge, to any person obtaining 8 | a copy of this software and associated documentation files (the 9 | 'Software'), to deal in the Software without restriction, including 10 | without limitation the rights to use, copy, modify, merge, publish, 11 | distribute, sublicense, and/or sell copies of the Software, and to 12 | permit persons to whom the Software is furnished to do so, subject to 13 | the following conditions: 14 | 15 | The above copyright notice and this permission notice shall be 16 | included in all copies or substantial portions of the Software. 17 | 18 | THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, 19 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 20 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 21 | IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 22 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 23 | TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 24 | SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 25 | 26 | */.handsontable{position:relative}.handsontable .hide{display:none}.handsontable .relative{position:relative}.handsontable.htAutoSize{visibility:hidden;left:-99000px;position:absolute;top:-99000px}.handsontable .wtHider{width:0}.handsontable .wtSpreader{position:relative;width:0;height:auto}.handsontable table,.handsontable tbody,.handsontable thead,.handsontable td,.handsontable th,.handsontable input,.handsontable textarea,.handsontable div{box-sizing:content-box;-webkit-box-sizing:content-box;-moz-box-sizing:content-box}.handsontable input,.handsontable textarea{min-height:initial}.handsontable table.htCore{border-collapse:separate;border-spacing:0;margin:0;border-width:0;table-layout:fixed;width:0;outline-width:0;max-width:none;max-height:none}.handsontable col{width:50px}.handsontable col.rowHeader{width:50px}.handsontable th,.handsontable td{border-top-width:0;border-left-width:0;border-right:1px solid #CCC;border-bottom:1px solid #CCC;height:22px;empty-cells:show;line-height:21px;padding:0 4px 0 4px;background-color:#FFF;vertical-align:top;overflow:hidden;outline-width:0;white-space:pre-line;background-clip:padding-box}.handsontable td.htInvalid{background-color:#ff4c42!important}.handsontable td.htNoWrap{white-space:nowrap}.handsontable th:last-child{border-right:1px solid #CCC;border-bottom:1px solid #CCC}.handsontable tr:first-child th.htNoFrame,.handsontable th:first-child.htNoFrame,.handsontable th.htNoFrame{border-left-width:0;background-color:white;border-color:#FFF}.handsontable th:first-child,.handsontable th:nth-child(2),.handsontable td:first-of-type,.handsontable .htNoFrame+th,.handsontable .htNoFrame+td{border-left:1px solid #CCC}.handsontable.htRowHeaders thead tr th:nth-child(2){border-left:1px solid #CCC}.handsontable tr:first-child th,.handsontable tr:first-child td{border-top:1px solid #CCC}.ht_master:not(.innerBorderLeft):not(.emptyColumns) ~ .handsontable tbody tr th,.ht_master:not(.innerBorderLeft):not(.emptyColumns) ~ .handsontable:not(.ht_clone_top) thead tr th:first-child{border-right-width:0}.ht_master:not(.innerBorderTop) thead tr:last-child th,.ht_master:not(.innerBorderTop) ~ .handsontable thead tr:last-child th,.ht_master:not(.innerBorderTop) thead tr.lastChild th,.ht_master:not(.innerBorderTop) ~ .handsontable thead tr.lastChild th{border-bottom-width:0}.handsontable th{background-color:#f3f3f3;color:#222;text-align:center;font-weight:normal;white-space:nowrap}.handsontable thead th{padding:0}.handsontable th.active{background-color:#CCC}.handsontable thead th .relative{padding:2px 4px}.handsontable tbody th.ht__highlight,.handsontable thead th.ht__highlight{background-color:#dcdcdc}.handsontable.ht__selection--columns thead th.ht__highlight,.handsontable.ht__selection--rows tbody th.ht__highlight{background-color:#8eb0e7;color:#000}.handsontable .manualColumnResizer{position:fixed;top:0;cursor:col-resize;z-index:110;width:5px;height:25px}.handsontable .manualRowResizer{position:fixed;left:0;cursor:row-resize;z-index:110;height:5px;width:50px}.handsontable .manualColumnResizer:hover,.handsontable .manualColumnResizer.active,.handsontable .manualRowResizer:hover,.handsontable .manualRowResizer.active{background-color:#AAB}.handsontable .manualColumnResizerGuide{position:fixed;right:0;top:0;background-color:#AAB;display:none;width:0;border-right:1px dashed #777;margin-left:5px}.handsontable .manualRowResizerGuide{position:fixed;left:0;bottom:0;background-color:#AAB;display:none;height:0;border-bottom:1px dashed #777;margin-top:5px}.handsontable .manualColumnResizerGuide.active,.handsontable .manualRowResizerGuide.active{display:block;z-index:199}.handsontable .columnSorting{position:relative}.handsontable .columnSorting:hover{text-decoration:underline;cursor:pointer}.handsontable .columnSorting.ascending::after{content:'\25B2';color:#5f5f5f;position:absolute;right:-15px}.handsontable .columnSorting.descending::after{content:'\25BC';color:#5f5f5f;position:absolute;right:-15px}.handsontable .wtBorder{position:absolute;font-size:0}.handsontable .wtBorder.hidden{display:none!important}.handsontable td.area{background:-moz-linear-gradient(top,rgba(181,209,255,0.34) 0,rgba(181,209,255,0.34) 100%);background:-webkit-gradient(linear,left top,left bottom,color-stop(0%,rgba(181,209,255,0.34)),color-stop(100%,rgba(181,209,255,0.34)));background:-webkit-linear-gradient(top,rgba(181,209,255,0.34) 0,rgba(181,209,255,0.34) 100%);background:-o-linear-gradient(top,rgba(181,209,255,0.34) 0,rgba(181,209,255,0.34) 100%);background:-ms-linear-gradient(top,rgba(181,209,255,0.34) 0,rgba(181,209,255,0.34) 100%);background:linear-gradient(to bottom,rgba(181,209,255,0.34) 0,rgba(181,209,255,0.34) 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#57b5d1ff',endColorstr='#57b5d1ff',GradientType=0);background-color:#fff}.handsontable .wtBorder.corner{font-size:0;cursor:crosshair}.handsontable .htBorder.htFillBorder{background:red;width:1px;height:1px}.handsontableInput{border:0;outline-width:0;margin:0;padding:1px 5px 0 5px;font-family:inherit;line-height:21px;font-size:inherit;box-shadow:0 0 0 2px #5292f7 inset;resize:none;display:inline-block;color:#000;border-radius:0;background-color:#FFF}.handsontableInputHolder{position:absolute;top:0;left:0;z-index:100}.htSelectEditor{-webkit-appearance:menulist-button!important;position:absolute;width:auto}.handsontable .htDimmed{color:#777}.handsontable .htSubmenu{position:relative}.handsontable .htSubmenu :after{content:'▶';color:#777;position:absolute;right:5px}.handsontable .htLeft{text-align:left}.handsontable .htCenter{text-align:center}.handsontable .htRight{text-align:right}.handsontable .htJustify{text-align:justify}.handsontable .htTop{vertical-align:top}.handsontable .htMiddle{vertical-align:middle}.handsontable .htBottom{vertical-align:bottom}.handsontable .htPlaceholder{color:#999}.handsontable .htAutocompleteArrow{float:right;font-size:10px;color:#EEE;cursor:default;width:16px;text-align:center}.handsontable td .htAutocompleteArrow:hover{color:#777}.handsontable td.area .htAutocompleteArrow{color:#d3d3d3}.handsontable .htCheckboxRendererInput{display:inline-block;vertical-align:middle}.handsontable .htCheckboxRendererInput.noValue{opacity:.5}.handsontable .htCheckboxRendererLabel{cursor:pointer;display:inline-block;width:100%}@-webkit-keyframes opacity-hide{from{opacity:1}to{opacity:0}}@keyframes opacity-hide{from{opacity:1}to{opacity:0}}@-webkit-keyframes opacity-show{from{opacity:0}to{opacity:1}}@keyframes opacity-show{from{opacity:0}to{opacity:1}}.handsontable .handsontable.ht_clone_top .wtHider{padding:0 0 5px 0}.handsontable .autocompleteEditor.handsontable{padding-right:17px}.handsontable .autocompleteEditor.handsontable.htMacScroll{padding-right:15px}.handsontable.listbox{margin:0}.handsontable.listbox .ht_master table{border:1px solid #ccc;border-collapse:separate;background:white}.handsontable.listbox th,.handsontable.listbox tr:first-child th,.handsontable.listbox tr:last-child th,.handsontable.listbox tr:first-child td,.handsontable.listbox td{border-color:transparent}.handsontable.listbox th,.handsontable.listbox td{white-space:nowrap;text-overflow:ellipsis}.handsontable.listbox td.htDimmed{cursor:default;color:inherit;font-style:inherit}.handsontable.listbox .wtBorder{visibility:hidden}.handsontable.listbox tr td.current,.handsontable.listbox tr:hover td{background:#eee}.ht_clone_top{z-index:101}.ht_clone_left{z-index:102}.ht_clone_top_left_corner,.ht_clone_bottom_left_corner{z-index:103}.ht_clone_debug{z-index:103}.handsontable td.htSearchResult{background:#fcedd9;color:#583707}.htBordered{border-width:1px}.htBordered.htTopBorderSolid{border-top-style:solid;border-top-color:#000}.htBordered.htRightBorderSolid{border-right-style:solid;border-right-color:#000}.htBordered.htBottomBorderSolid{border-bottom-style:solid;border-bottom-color:#000}.htBordered.htLeftBorderSolid{border-left-style:solid;border-left-color:#000}.handsontable tbody tr th:nth-last-child(2){border-right:1px solid #CCC}.handsontable thead tr:nth-last-child(2) th.htGroupIndicatorContainer{border-bottom:1px solid #CCC;padding-bottom:5px}.ht_clone_top_left_corner thead tr th:nth-last-child(2){border-right:1px solid #CCC}.htCollapseButton{width:10px;height:10px;line-height:10px;text-align:center;border-radius:5px;border:1px solid #f3f3f3;-webkit-box-shadow:1px 1px 3px rgba(0,0,0,0.4);box-shadow:1px 1px 3px rgba(0,0,0,0.4);cursor:pointer;margin-bottom:3px;position:relative}.htCollapseButton:after{content:"";height:300%;width:1px;display:block;background:#ccc;margin-left:4px;position:absolute;bottom:10px}thead .htCollapseButton{right:5px;position:absolute;top:5px;background:#fff}thead .htCollapseButton:after{height:1px;width:700%;right:10px;top:4px}.handsontable tr th .htExpandButton{position:absolute;width:10px;height:10px;line-height:10px;text-align:center;border-radius:5px;border:1px solid #f3f3f3;-webkit-box-shadow:1px 1px 3px rgba(0,0,0,0.4);box-shadow:1px 1px 3px rgba(0,0,0,0.4);cursor:pointer;top:0;display:none}.handsontable thead tr th .htExpandButton{top:5px}.handsontable tr th .htExpandButton.clickable{display:block}.collapsibleIndicator{position:absolute;top:50%;transform:translate(0%,-50%);right:5px;border:1px solid #a6a6a6;line-height:10px;color:#222;border-radius:10px;font-size:10px;width:10px;height:10px;cursor:pointer;-webkit-box-shadow:0 0 0 6px rgba(238,238,238,1);-moz-box-shadow:0 0 0 6px rgba(238,238,238,1);box-shadow:0 0 0 6px rgba(238,238,238,1);background:#eee}.handsontable col.hidden{width:0!important}.handsontable table tr th.lightRightBorder{border-right:1px solid #e6e6e6}.handsontable tr.hidden,.handsontable tr.hidden td,.handsontable tr.hidden th{display:none}.ht_master,.ht_clone_left,.ht_clone_top,.ht_clone_bottom{overflow:hidden}.ht_master .wtHolder{overflow:auto}.ht_clone_left .wtHolder{overflow-x:hidden;overflow-y:auto}.ht_clone_top .wtHolder,.ht_clone_bottom .wtHolder{overflow-x:auto;overflow-y:hidden}.wtDebugHidden{display:none}.wtDebugVisible{display:block;-webkit-animation-duration:.5s;-webkit-animation-name:wtFadeInFromNone;animation-duration:.5s;animation-name:wtFadeInFromNone}@keyframes wtFadeInFromNone{0%{display:none;opacity:0}1%{display:block;opacity:0}100%{display:block;opacity:1}}@-webkit-keyframes wtFadeInFromNone{0%{display:none;opacity:0}1%{display:block;opacity:0}100%{display:block;opacity:1}}.handsontable.mobile,.handsontable.mobile .wtHolder{-webkit-touch-callout:none;-webkit-user-select:none;-khtml-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;-webkit-tap-highlight-color:rgba(0,0,0,0);-webkit-overflow-scrolling:touch}.htMobileEditorContainer{display:none;position:absolute;top:0;width:70%;height:54pt;background:#f8f8f8;border-radius:20px;border:1px solid #ebebeb;z-index:999;box-sizing:border-box;-webkit-box-sizing:border-box;-webkit-text-size-adjust:none}.topLeftSelectionHandle:not(.ht_master .topLeftSelectionHandle),.topLeftSelectionHandle-HitArea:not(.ht_master .topLeftSelectionHandle-HitArea){z-index:9999}.topLeftSelectionHandle,.topLeftSelectionHandle-HitArea,.bottomRightSelectionHandle,.bottomRightSelectionHandle-HitArea{left:-10000px;top:-10000px}.htMobileEditorContainer.active{display:block}.htMobileEditorContainer .inputs{position:absolute;right:210pt;bottom:10pt;top:10pt;left:14px;height:34pt}.htMobileEditorContainer .inputs textarea{font-size:13pt;border:1px solid #a1a1a1;-webkit-appearance:none;-webkit-box-shadow:none;-moz-box-shadow:none;box-shadow:none;position:absolute;left:14px;right:14px;top:0;bottom:0;padding:7pt}.htMobileEditorContainer .cellPointer{position:absolute;top:-13pt;height:0;width:0;left:30px;border-left:13pt solid transparent;border-right:13pt solid transparent;border-bottom:13pt solid #ebebeb}.htMobileEditorContainer .cellPointer.hidden{display:none}.htMobileEditorContainer .cellPointer:before{content:'';display:block;position:absolute;top:2px;height:0;width:0;left:-13pt;border-left:13pt solid transparent;border-right:13pt solid transparent;border-bottom:13pt solid #f8f8f8}.htMobileEditorContainer .moveHandle{position:absolute;top:10pt;left:5px;width:30px;bottom:0;cursor:move;z-index:9999}.htMobileEditorContainer .moveHandle:after{content:"..\a..\a..\a..";white-space:pre;line-height:10px;font-size:20pt;display:inline-block;margin-top:-8px;color:#ebebeb}.htMobileEditorContainer .positionControls{width:205pt;position:absolute;right:5pt;top:0;bottom:0}.htMobileEditorContainer .positionControls>div{width:50pt;height:100%;float:left}.htMobileEditorContainer .positionControls>div:after{content:" ";display:block;width:15pt;height:15pt;text-align:center;line-height:50pt}.htMobileEditorContainer .leftButton:after,.htMobileEditorContainer .rightButton:after,.htMobileEditorContainer .upButton:after,.htMobileEditorContainer .downButton:after{transform-origin:5pt 5pt;-webkit-transform-origin:5pt 5pt;margin:21pt 0 0 21pt}.htMobileEditorContainer .leftButton:after{border-top:2px solid #288ffe;border-left:2px solid #288ffe;-webkit-transform:rotate(-45deg)}.htMobileEditorContainer .leftButton:active:after{border-color:#cfcfcf}.htMobileEditorContainer .rightButton:after{border-top:2px solid #288ffe;border-left:2px solid #288ffe;-webkit-transform:rotate(135deg)}.htMobileEditorContainer .rightButton:active:after{border-color:#cfcfcf}.htMobileEditorContainer .upButton:after{border-top:2px solid #288ffe;border-left:2px solid #288ffe;-webkit-transform:rotate(45deg)}.htMobileEditorContainer .upButton:active:after{border-color:#cfcfcf}.htMobileEditorContainer .downButton:after{border-top:2px solid #288ffe;border-left:2px solid #288ffe;-webkit-transform:rotate(225deg)}.htMobileEditorContainer .downButton:active:after{border-color:#cfcfcf}.handsontable.hide-tween{-webkit-animation:opacity-hide .3s;animation:opacity-hide .3s;animation-fill-mode:forwards;-webkit-animation-fill-mode:forwards}.handsontable.show-tween{-webkit-animation:opacity-show .3s;animation:opacity-show .3s;animation-fill-mode:forwards;-webkit-animation-fill-mode:forwards}.htCommentCell{position:relative}.htCommentCell:after{content:'';position:absolute;top:0;right:0;border-left:6px solid transparent;border-top:6px solid black}.htComments{display:none;z-index:1059;position:absolute}.htCommentTextArea{box-shadow:rgba(0,0,0,0.117647) 0 1px 3px,rgba(0,0,0,0.239216) 0 1px 2px;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;border:0;border-left:3px solid #ccc;background-color:#fff;width:215px;height:90px;font-size:12px;padding:5px;outline:0!important;-webkit-appearance:none}.htCommentTextArea:focus{box-shadow:rgba(0,0,0,0.117647) 0 1px 3px,rgba(0,0,0,0.239216) 0 1px 2px,inset 0 0 0 1px #5292f7;border-left:3px solid #5292f7}/*! 27 | * Handsontable ContextMenu 28 | */.htContextMenu{display:none;position:absolute;z-index:1060}.htContextMenu .ht_clone_top,.htContextMenu .ht_clone_left,.htContextMenu .ht_clone_corner,.htContextMenu .ht_clone_debug{display:none}.htContextMenu table.htCore{border:1px solid #ccc;border-bottom-width:2px;border-right-width:2px}.htContextMenu .wtBorder{visibility:hidden}.htContextMenu table tbody tr td{background:white;border-width:0;padding:4px 6px 0 6px;cursor:pointer;overflow:hidden;white-space:nowrap;text-overflow:ellipsis}.htContextMenu table tbody tr td:first-child{border:0}.htContextMenu table tbody tr td.htDimmed{font-style:normal;color:#323232}.htContextMenu table tbody tr td.current,.htContextMenu table tbody tr td.zeroclipboard-is-hover{background:#f3f3f3}.htContextMenu table tbody tr td.htSeparator{border-top:1px solid #bbb;height:0;padding:0;cursor:default}.htContextMenu table tbody tr td.htDisabled{color:#999;cursor:default}.htContextMenu table tbody tr td.htDisabled:hover{background:#fff;color:#999;cursor:default}.htContextMenu table tbody tr.htHidden{display:none}.htContextMenu table tbody tr td .htItemWrapper{margin-left:10px;margin-right:6px}.htContextMenu table tbody tr td div span.selected{margin-top:-2px;position:absolute;left:4px}.htContextMenu .ht_master .wtHolder{overflow:hidden}.htRowHeaders .ht_master.innerBorderLeft ~ .ht_clone_top_left_corner th:nth-child(2),.htRowHeaders .ht_master.innerBorderLeft ~ .ht_clone_left td:first-of-type{border-left:0 none}.handsontable .wtHider{position:relative}.handsontable.ht__manualColumnMove.after-selection--columns thead th.ht__highlight{cursor:move;cursor:-moz-grab;cursor:-webkit-grab;cursor:grab}.handsontable.ht__manualColumnMove.on-moving--columns,.handsontable.ht__manualColumnMove.on-moving--columns thead th.ht__highlight{cursor:move;cursor:-moz-grabbing;cursor:-webkit-grabbing;cursor:grabbing}.handsontable.ht__manualColumnMove.on-moving--columns .manualColumnResizer{display:none}.handsontable .ht__manualColumnMove--guideline,.handsontable .ht__manualColumnMove--backlight{position:absolute;height:100%;display:none}.handsontable .ht__manualColumnMove--guideline{background:#757575;width:2px;top:0;margin-left:-1px;z-index:105}.handsontable .ht__manualColumnMove--backlight{background:#343434;background:rgba(52,52,52,0.25);display:none;z-index:105;pointer-events:none}.handsontable.on-moving--columns.show-ui .ht__manualColumnMove--guideline,.handsontable.on-moving--columns .ht__manualColumnMove--backlight{display:block}.handsontable .wtHider{position:relative}.handsontable.ht__manualRowMove.after-selection--rows tbody th.ht__highlight{cursor:move;cursor:-moz-grab;cursor:-webkit-grab;cursor:grab}.handsontable.ht__manualRowMove.on-moving--rows,.handsontable.ht__manualRowMove.on-moving--rows tbody th.ht__highlight{cursor:move;cursor:-moz-grabbing;cursor:-webkit-grabbing;cursor:grabbing}.handsontable.ht__manualRowMove.on-moving--rows .manualRowResizer{display:none}.handsontable .ht__manualRowMove--guideline,.handsontable .ht__manualRowMove--backlight{position:absolute;width:100%;display:none}.handsontable .ht__manualRowMove--guideline{background:#757575;height:2px;left:0;margin-top:-1px;z-index:105}.handsontable .ht__manualRowMove--backlight{background:#343434;background:rgba(52,52,52,0.25);display:none;z-index:105;pointer-events:none}.handsontable.on-moving--rows.show-ui .ht__manualRowMove--guideline,.handsontable.on-moving--rows .ht__manualRowMove--backlight{display:block}/*! 29 | * Pikaday 30 | * Copyright © 2014 David Bushell | BSD & MIT license | http://dbushell.com/ 31 | */.pika-single{z-index:9999;display:block;position:relative;color:#333;background:#fff;border:1px solid #ccc;border-bottom-color:#bbb;font-family:"Helvetica Neue",Helvetica,Arial,sans-serif}.pika-single:before,.pika-single:after{content:" ";display:table}.pika-single:after{clear:both}.pika-single{*zoom:1}.pika-single.is-hidden{display:none}.pika-single.is-bound{position:absolute;box-shadow:0 5px 15px -5px rgba(0,0,0,.5)}.pika-lendar{float:left;width:240px;margin:8px}.pika-title{position:relative;text-align:center}.pika-label{display:inline-block;*display:inline;position:relative;z-index:9999;overflow:hidden;margin:0;padding:5px 3px;font-size:14px;line-height:20px;font-weight:bold;background-color:#fff}.pika-title select{cursor:pointer;position:absolute;z-index:9998;margin:0;left:0;top:5px;filter:alpha(opacity=0);opacity:0}.pika-prev,.pika-next{display:block;cursor:pointer;position:relative;outline:0;border:0;padding:0;width:20px;height:30px;text-indent:20px;white-space:nowrap;overflow:hidden;background-color:transparent;background-position:center center;background-repeat:no-repeat;background-size:75% 75%;opacity:.5;*position:absolute;*top:0}.pika-prev:hover,.pika-next:hover{opacity:1}.pika-prev,.is-rtl .pika-next{float:left;background-image:url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAeCAYAAAAsEj5rAAAAUklEQVR42u3VMQoAIBADQf8Pgj+OD9hG2CtONJB2ymQkKe0HbwAP0xucDiQWARITIDEBEnMgMQ8S8+AqBIl6kKgHiXqQqAeJepBo/z38J/U0uAHlaBkBl9I4GwAAAABJRU5ErkJggg==');*left:0}.pika-next,.is-rtl .pika-prev{float:right;background-image:url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAeCAYAAAAsEj5rAAAAU0lEQVR42u3VOwoAMAgE0dwfAnNjU26bYkBCFGwfiL9VVWoO+BJ4Gf3gtsEKKoFBNTCoCAYVwaAiGNQGMUHMkjGbgjk2mIONuXo0nC8XnCf1JXgArVIZAQh5TKYAAAAASUVORK5CYII=');*right:0}.pika-prev.is-disabled,.pika-next.is-disabled{cursor:default;opacity:.2}.pika-select{display:inline-block;*display:inline}.pika-table{width:100%;border-collapse:collapse;border-spacing:0;border:0}.pika-table th,.pika-table td{width:14.285714285714286%;padding:0}.pika-table th{color:#999;font-size:12px;line-height:25px;font-weight:bold;text-align:center}.pika-button{cursor:pointer;display:block;box-sizing:border-box;-moz-box-sizing:border-box;outline:0;border:0;margin:0;width:100%;padding:5px;color:#666;font-size:12px;line-height:15px;text-align:right;background:#f5f5f5}.pika-week{font-size:11px;color:#999}.is-today .pika-button{color:#3af;font-weight:bold}.is-selected .pika-button{color:#fff;font-weight:bold;background:#3af;box-shadow:inset 0 1px 3px #178fe5;border-radius:3px}.is-inrange .pika-button{background:#d5e9f7}.is-startrange .pika-button{color:#fff;background:#6cb31d;box-shadow:none;border-radius:3px}.is-endrange .pika-button{color:#fff;background:#3af;box-shadow:none;border-radius:3px}.is-disabled .pika-button,.is-outside-current-month .pika-button{pointer-events:none;cursor:default;color:#999;opacity:.3}.pika-button:hover{color:#fff;background:#ff8000;box-shadow:none;border-radius:3px}.pika-table abbr{border-bottom:0;cursor:help} -------------------------------------------------------------------------------- /docs/source/_themes/djangodocs-epub/epub-cover.html: -------------------------------------------------------------------------------- 1 | {%- extends "epub/epub-cover.html" %} 2 | 3 | {% block content %} 4 |
5 |

Django Documentation

6 |

Release {{ release }}

7 |

{{ copyright }}

8 |

{{ last_updated }}

9 |
10 | {% endblock %} 11 | -------------------------------------------------------------------------------- /docs/source/_themes/djangodocs-epub/static/docicons-behindscenes.png: -------------------------------------------------------------------------------- 1 | ../../djangodocs/static/docicons-behindscenes.png -------------------------------------------------------------------------------- /docs/source/_themes/djangodocs-epub/static/docicons-note.png: -------------------------------------------------------------------------------- 1 | ../../djangodocs/static/docicons-note.png -------------------------------------------------------------------------------- /docs/source/_themes/djangodocs-epub/static/docicons-philosophy.png: -------------------------------------------------------------------------------- 1 | ../../djangodocs/static/docicons-philosophy.png -------------------------------------------------------------------------------- /docs/source/_themes/djangodocs-epub/static/docicons-warning.png: -------------------------------------------------------------------------------- 1 | ../../djangodocs/static/docicons-warning.png -------------------------------------------------------------------------------- /docs/source/_themes/djangodocs-epub/static/epub.css: -------------------------------------------------------------------------------- 1 | h1 { margin-top: 0; } 2 | 3 | /* Keep lists a bit narrow to maximize page estate regarding width. */ 4 | ol, ul { 5 | margin: 0; 6 | padding: 0 0 0 1.3em; 7 | } 8 | 9 | /* Images should never exceed the width of the page. */ 10 | img { max-width: 100%; } 11 | 12 | /* Don't display URL after links, this is not print. */ 13 | .link-target { display: none; } 14 | 15 | /* This is the front cover page of the book. */ 16 | .epub-cover { text-align: center; } 17 | .epub-cover h1 { margin: 4em 0 0 0; } 18 | .epub-cover h2 { margin: 1em 0; } 19 | .epub-cover h3 { margin: 3em 0 2em 0; } 20 | 21 | /* Code examples should never exceed the width of the page, so wrap instead. */ 22 | pre, span.pre { white-space: pre-wrap; } 23 | 24 | pre { 25 | background-color: #f6f6f6; 26 | border: 0; 27 | padding: 0.5em; 28 | font-size: 90%; 29 | } 30 | 31 | /* Header for some code blocks. */ 32 | .snippet-filename { 33 | background-color: #393939; 34 | color: white; 35 | margin: 0; 36 | padding: 0.5em; 37 | font: bold 90% monospace; 38 | } 39 | .snippet-filename + .highlight > pre, 40 | .snippet-filename + pre { 41 | margin-top: 0; 42 | } 43 | 44 | a:link, a:visited { color: #396623; } 45 | a:hover { color: #1d3311; } 46 | 47 | /* Use special styled note boxes from the default theme, but with the left side 48 | fitted after the icon, to allow text resizing with breaking. */ 49 | .note, .admonition { 50 | background-position: 9px 0.8em; 51 | background-repeat: no-repeat; 52 | padding: 0.8em 1em 0.8em 65px; 53 | margin: 1em 0; 54 | border: 0.01em solid black; 55 | } 56 | 57 | .note, .admonition { background-image: url(docicons-note.png); } 58 | div.admonition-philosophy { background-image: url(docicons-philosophy.png); } 59 | div.admonition-behind-the-scenes { background-image: url(docicons-behindscenes.png); } 60 | .admonition.warning { background-image: url(docicons-warning.png); } 61 | 62 | .admonition-title { 63 | font-weight: bold; 64 | margin: 0; 65 | } 66 | 67 | .admonition .last { margin-bottom: 0; } 68 | -------------------------------------------------------------------------------- /docs/source/_themes/djangodocs-epub/theme.conf: -------------------------------------------------------------------------------- 1 | [theme] 2 | inherit = epub 3 | stylesheet = epub.css 4 | pygments_style = trac 5 | 6 | [options] 7 | relbar1 = false 8 | footer = false 9 | -------------------------------------------------------------------------------- /docs/source/_themes/djangodocs/genindex.html: -------------------------------------------------------------------------------- 1 | {% extends "basic/genindex.html" %} 2 | 3 | {% block bodyclass %}{% endblock %} 4 | {% block sidebarwrapper %}{% endblock %} 5 | -------------------------------------------------------------------------------- /docs/source/_themes/djangodocs/layout.html: -------------------------------------------------------------------------------- 1 | {% extends "basic/layout.html" %} 2 | 3 | {%- macro secondnav() %} 4 | {%- if prev %} 5 | « previous 6 | {{ reldelim2 }} 7 | {%- endif %} 8 | {%- if parents %} 9 | up 10 | {%- else %} 11 | up 12 | {%- endif %} 13 | {%- if next %} 14 | {{ reldelim2 }} 15 | next » 16 | {%- endif %} 17 | {%- endmacro %} 18 | 19 | {% block extrahead %} 20 | {# When building htmlhelp (CHM format) disable jQuery inclusion, #} 21 | {# as it causes problems in compiled CHM files. #} 22 | {% if builder != "htmlhelp" %} 23 | {{ super() }} 24 | 25 | 26 | 27 | 30 | 62 | {% endif %} 63 | {% endblock %} 64 | 65 | {% block document %} 66 |
67 |
68 |

{{ docstitle }}

69 | 75 | 76 |
77 | 78 |
79 |
80 |
81 |
82 | {% block body %}{% endblock %} 83 |
84 |
85 |
86 | {% block sidebarwrapper %} 87 | {% if pagename != 'index' %} 88 | 95 | {% endif %} 96 | {% endblock %} 97 |
98 | 99 |
100 | 101 |
102 |
103 | {% endblock %} 104 | 105 | {% block sidebarrel %} 106 |

Browse

107 | 115 |

You are here:

116 |
    117 |
  • 118 | {{ docstitle }} 119 | {% for p in parents %} 120 |
    • {{ p.title }} 121 | {% endfor %} 122 |
      • {{ title }}
      123 | {% for p in parents %}
    {% endfor %} 124 |
  • 125 |
126 | {% endblock %} 127 | 128 | {# Empty some default blocks out #} 129 | {% block relbar1 %}{% endblock %} 130 | {% block relbar2 %}{% endblock %} 131 | {% block sidebar1 %}{% endblock %} 132 | {% block sidebar2 %}{% endblock %} 133 | {% block footer %}{% endblock %} 134 | -------------------------------------------------------------------------------- /docs/source/_themes/djangodocs/modindex.html: -------------------------------------------------------------------------------- 1 | {% extends "basic/modindex.html" %} 2 | {% block bodyclass %}{% endblock %} 3 | {% block sidebarwrapper %}{% endblock %} 4 | -------------------------------------------------------------------------------- /docs/source/_themes/djangodocs/search.html: -------------------------------------------------------------------------------- 1 | {% extends "basic/search.html" %} 2 | {% block bodyclass %}{% endblock %} 3 | {% block sidebarwrapper %}{% endblock %} 4 | -------------------------------------------------------------------------------- /docs/source/_themes/djangodocs/static/default.css: -------------------------------------------------------------------------------- 1 | @import url(reset-fonts-grids.css); 2 | @import url(djangodocs.css); 3 | @import url(homepage.css); 4 | -------------------------------------------------------------------------------- /docs/source/_themes/djangodocs/static/djangodocs.css: -------------------------------------------------------------------------------- 1 | /*** setup ***/ 2 | html { background:#092e20;} 3 | body { font:12px/1.5 Verdana,sans-serif; background:#092e20; color: white;} 4 | #custom-doc { width:76.54em;*width:74.69em;min-width:995px; max-width:100em; margin:auto; text-align:left; padding-top:16px; margin-top:0;} 5 | #hd { padding: 4px 0 12px 0; } 6 | #bd { background:#234F32; } 7 | #ft { color:#487858; font-size:90%; padding-bottom: 2em; } 8 | 9 | /*** links ***/ 10 | a {text-decoration: none;} 11 | a img {border: none;} 12 | a:link, a:visited { color:#ffc757; } 13 | #bd a:link, #bd a:visited { color:#ab5603; text-decoration:underline; } 14 | #bd #sidebar a:link, #bd #sidebar a:visited { color:#ffc757; text-decoration:none; } 15 | a:hover { color:#ffe761; } 16 | #bd a:hover { background-color:#E0FFB8; color:#234f32; text-decoration:none; } 17 | #bd #sidebar a:hover { color:#ffe761; background:none; } 18 | h2 a, h3 a, h4 a { text-decoration:none !important; } 19 | a.reference em { font-style: normal; } 20 | 21 | /*** sidebar ***/ 22 | #sidebar div.sphinxsidebarwrapper { font-size:92%; margin-right: 14px; } 23 | #sidebar h3, #sidebar h4 { color: white; font-size: 125%; } 24 | #sidebar a { color: white; } 25 | #sidebar ul ul { margin-top:0; margin-bottom:0; } 26 | #sidebar li { margin-top: 0.2em; margin-bottom: 0.2em; } 27 | 28 | /*** nav ***/ 29 | div.nav { margin: 0; font-size: 11px; text-align: right; color: #487858;} 30 | #hd div.nav { margin-top: -27px; } 31 | #ft div.nav { margin-bottom: -18px; } 32 | #hd h1 a { color: white; } 33 | #global-nav { position:absolute; top:5px; margin-left: -5px; padding:7px 0; color:#263E2B; } 34 | #global-nav a:link, #global-nav a:visited {color:#487858;} 35 | #global-nav a {padding:0 4px;} 36 | #global-nav a.about {padding-left:0;} 37 | #global-nav:hover {color:#fff;} 38 | #global-nav:hover a:link, #global-nav:hover a:visited { color:#ffc757; } 39 | 40 | /*** content ***/ 41 | #yui-main div.yui-b { position: relative; } 42 | #yui-main div.yui-b { margin: 0 0 0 20px; background: white; color: black; padding: 0.3em 2em 1em 2em; } 43 | 44 | /*** basic styles ***/ 45 | dd { margin-left:15px; } 46 | h1,h2,h3,h4 { margin-top:1em; font-family:"Trebuchet MS",sans-serif; font-weight:normal; } 47 | h1 { font-size:218%; margin-top:0.6em; margin-bottom:.4em; line-height:1.1em; } 48 | h2 { font-size:175%; margin-bottom:.6em; line-height:1.2em; color:#092e20; } 49 | h3 { font-size:150%; font-weight:bold; margin-bottom:.2em; color:#487858; } 50 | h4 { font-size:125%; font-weight:bold; margin-top:1.5em; margin-bottom:3px; } 51 | div.figure { text-align: center; } 52 | div.figure p.caption { font-size:1em; margin-top:0; margin-bottom:1.5em; color: #555;} 53 | hr { color:#ccc; background-color:#ccc; height:1px; border:0; } 54 | p, ul, dl { margin-top:.6em; margin-bottom:1em; padding-bottom: 0.1em;} 55 | #yui-main div.yui-b img { max-width: 50em; margin-left: auto; margin-right: auto; display: block; } 56 | caption { font-size:1em; font-weight:bold; margin-top:0.5em; margin-bottom:0.5em; margin-left: 2px; text-align: center; } 57 | blockquote { padding: 0 1em; margin: 1em 0; font:125%/1.2em "Trebuchet MS", sans-serif; color:#234f32; border-left:2px solid #94da3a; } 58 | strong { font-weight: bold; } 59 | em { font-style: italic; } 60 | ins { font-weight: bold; text-decoration: none; } 61 | 62 | /*** lists ***/ 63 | ul { padding-left:30px; } 64 | ol { padding-left:30px; } 65 | ol.arabic li { list-style-type: decimal; } 66 | ul li { list-style-type:square; margin-bottom:.4em; } 67 | ul ul li { list-style-type:disc; } 68 | ul ul ul li { list-style-type:circle; } 69 | ol li { margin-bottom: .4em; } 70 | ul ul { padding-left:1.2em; } 71 | ul ul ul { padding-left:1em; } 72 | ul.linklist, ul.toc { padding-left:0; } 73 | ul.toc ul { margin-left:.6em; } 74 | ul.toc ul li { list-style-type:square; } 75 | ul.toc ul ul li { list-style-type:disc; } 76 | ul.linklist li, ul.toc li { list-style-type:none; } 77 | dt { font-weight:bold; margin-top:.5em; font-size:1.1em; } 78 | dd { margin-bottom:.8em; } 79 | ol.toc { margin-bottom: 2em; } 80 | ol.toc li { font-size:125%; padding: .5em; line-height:1.2em; clear: right; } 81 | ol.toc li.b { background-color: #E0FFB8; } 82 | ol.toc li a:hover { background-color: transparent !important; text-decoration: underline !important; } 83 | ol.toc span.release-date { color:#487858; float: right; font-size: 85%; padding-right: .5em; } 84 | ol.toc span.comment-count { font-size: 75%; color: #999; } 85 | 86 | /*** tables ***/ 87 | table { color:#000; margin-bottom: 1em; width: 100%; } 88 | table.docutils td p { margin-top:0; margin-bottom:.5em; } 89 | table.docutils td, table.docutils th { border-bottom:1px solid #dfdfdf; padding:4px 2px;} 90 | table.docutils thead th { border-bottom:2px solid #dfdfdf; text-align:left; font-weight: bold; white-space: nowrap; } 91 | table.docutils thead th p { margin: 0; padding: 0; } 92 | table.docutils { border-collapse:collapse; } 93 | 94 | /*** code blocks ***/ 95 | .literal { color:#234f32; white-space:nowrap; } 96 | dt > tt.literal { white-space: normal; } 97 | #sidebar .literal { color:white; background:transparent; font-size:11px; } 98 | h4 .literal { color: #234f32; font-size: 13px; } 99 | pre { font-size:small; background:#E0FFB8; border:1px solid #94da3a; border-width:1px 0; margin: 1em 0; padding: .3em .4em; overflow: hidden; line-height: 1.3em;} 100 | dt .literal, table .literal { background:none; } 101 | #bd a.reference { text-decoration: none; } 102 | #bd a.reference tt.literal { border-bottom: 1px #234f32 dotted; } 103 | div.snippet-filename { color: white; background-color: #234F32; margin: 0; padding: 2px 5px; width: 100%; font-family: monospace; font-size: small; line-height: 1.3em; } 104 | div.snippet-filename + div.highlight > pre { margin-top: 0; } 105 | div.snippet-filename + pre { margin-top: 0; } 106 | 107 | /* Restore colors of pygments hyperlinked code */ 108 | #bd .highlight .k a:link, #bd .highlight .k a:visited { color: #000000; text-decoration: none; border-bottom: 1px dotted #000000; } 109 | #bd .highlight .nf a:link, #bd .highlight .nf a:visited { color: #990000; text-decoration: none; border-bottom: 1px dotted #990000; } 110 | 111 | 112 | /*** notes & admonitions ***/ 113 | .note, .admonition { padding:.8em 1em .8em; margin: 1em 0; border:1px solid #94da3a; } 114 | .admonition-title { font-weight:bold; margin-top:0 !important; margin-bottom:0 !important;} 115 | .admonition .last { margin-bottom:0 !important; } 116 | .note, .admonition { padding-left:65px; background:url(docicons-note.png) .8em .8em no-repeat;} 117 | div.admonition-philosophy { padding-left:65px; background:url(docicons-philosophy.png) .8em .8em no-repeat;} 118 | div.admonition-behind-the-scenes { padding-left:65px; background:url(docicons-behindscenes.png) .8em .8em no-repeat;} 119 | .admonition.warning { background:url(docicons-warning.png) .8em .8em no-repeat; border:1px solid #ffc83c;} 120 | 121 | /*** versoinadded/changes ***/ 122 | div.versionadded, div.versionchanged { } 123 | div.versionadded span.title, div.versionchanged span.title, span.versionmodified { font-weight: bold; } 124 | div.versionadded, div.versionchanged, div.deprecated { color:#555; } 125 | 126 | /*** p-links ***/ 127 | a.headerlink { color: #c60f0f; font-size: 0.8em; padding: 0 4px 0 4px; text-decoration: none; visibility: hidden; } 128 | h1:hover > a.headerlink, h2:hover > a.headerlink, h3:hover > a.headerlink, h4:hover > a.headerlink, h5:hover > a.headerlink, h6:hover > a.headerlink, dt:hover > a.headerlink { visibility: visible; } 129 | 130 | /*** index ***/ 131 | table.indextable td { text-align: left; vertical-align: top;} 132 | table.indextable dl, table.indextable dd { margin-top: 0; margin-bottom: 0; } 133 | table.indextable tr.pcap { height: 10px; } 134 | table.indextable tr.cap { margin-top: 10px; background-color: #f2f2f2;} 135 | 136 | /*** page-specific overrides ***/ 137 | div#contents ul { margin-bottom: 0;} 138 | div#contents ul li { margin-bottom: 0;} 139 | div#contents ul ul li { margin-top: 0.3em;} 140 | 141 | /*** IE hacks ***/ 142 | * pre { width: 100%; } 143 | -------------------------------------------------------------------------------- /docs/source/_themes/djangodocs/static/docicons-behindscenes.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pyexcel-webwares/django-excel/a17fea39ffcbf3b7c0d195cab5002ba8c8d416a9/docs/source/_themes/djangodocs/static/docicons-behindscenes.png -------------------------------------------------------------------------------- /docs/source/_themes/djangodocs/static/docicons-note.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pyexcel-webwares/django-excel/a17fea39ffcbf3b7c0d195cab5002ba8c8d416a9/docs/source/_themes/djangodocs/static/docicons-note.png -------------------------------------------------------------------------------- /docs/source/_themes/djangodocs/static/docicons-philosophy.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pyexcel-webwares/django-excel/a17fea39ffcbf3b7c0d195cab5002ba8c8d416a9/docs/source/_themes/djangodocs/static/docicons-philosophy.png -------------------------------------------------------------------------------- /docs/source/_themes/djangodocs/static/docicons-warning.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pyexcel-webwares/django-excel/a17fea39ffcbf3b7c0d195cab5002ba8c8d416a9/docs/source/_themes/djangodocs/static/docicons-warning.png -------------------------------------------------------------------------------- /docs/source/_themes/djangodocs/static/homepage.css: -------------------------------------------------------------------------------- 1 | #index p.rubric { font-size:150%; font-weight:normal; margin-bottom:.2em; color:#487858; } 2 | 3 | #index div.section dt { font-weight: normal; } 4 | 5 | #index #s-getting-help { float: right; width: 35em; background: #E1ECE2; padding: 1em; margin: 2em 0 2em 2em; } 6 | #index #s-getting-help h2 { margin: 0; } 7 | 8 | #index #s-django-documentation div.section div.section h3 { margin: 0; } 9 | #index #s-django-documentation div.section div.section { background: #E1ECE2; padding: 1em; margin: 2em 0 2em 40.3em; } 10 | #index #s-django-documentation div.section div.section a.reference { white-space: nowrap; } 11 | 12 | #index #s-using-django dl, 13 | #index #s-add-on-contrib-applications dl, 14 | #index #s-solving-specific-problems dl, 15 | #index #s-reference dl 16 | { float: left; width: 41em; } 17 | 18 | #index #s-add-on-contrib-applications, 19 | #index #s-solving-specific-problems, 20 | #index #s-reference, 21 | #index #s-and-all-the-rest 22 | { clear: left; } 23 | -------------------------------------------------------------------------------- /docs/source/_themes/djangodocs/static/reset-fonts-grids.css: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2008, Yahoo! Inc. All rights reserved. 3 | Code licensed under the BSD License: 4 | http://developer.yahoo.net/yui/license.txt 5 | version: 2.5.1 6 | */ 7 | html{color:#000;background:#FFF;}body,div,dl,dt,dd,ul,ol,li,h1,h2,h3,h4,h5,h6,pre,code,form,fieldset,legend,input,textarea,p,blockquote,th,td{margin:0;padding:0;}table{border-collapse:collapse;border-spacing:0;}fieldset,img{border:0;}address,caption,cite,code,dfn,em,strong,th,var{font-style:normal;font-weight:normal;}li{list-style:none;}caption,th{text-align:left;}h1,h2,h3,h4,h5,h6{font-size:100%;font-weight:normal;}q:before,q:after{content:'';}abbr,acronym {border:0;font-variant:normal;}sup {vertical-align:text-top;}sub {vertical-align:text-bottom;}input,textarea,select{font-family:inherit;font-size:inherit;font-weight:inherit;}input,textarea,select{*font-size:100%;}legend{color:#000;}body {font:13px/1.231 arial,helvetica,clean,sans-serif;*font-size:small;*font:x-small;}table {font-size:inherit;font:100%;}pre,code,kbd,samp,tt{font-family:monospace;*font-size:108%;line-height:100%;} 8 | body{text-align:center;}#ft{clear:both;}#doc,#doc2,#doc3,#doc4,.yui-t1,.yui-t2,.yui-t3,.yui-t4,.yui-t5,.yui-t6,.yui-t7{margin:auto;text-align:left;width:57.69em;*width:56.25em;min-width:750px;}#doc2{width:73.076em;*width:71.25em;}#doc3{margin:auto 10px;width:auto;}#doc4{width:74.923em;*width:73.05em;}.yui-b{position:relative;}.yui-b{_position:static;}#yui-main .yui-b{position:static;}#yui-main{width:100%;}.yui-t1 #yui-main,.yui-t2 #yui-main,.yui-t3 #yui-main{float:right;margin-left:-25em;}.yui-t4 #yui-main,.yui-t5 #yui-main,.yui-t6 #yui-main{float:left;margin-right:-25em;}.yui-t1 .yui-b{float:left;width:12.30769em;*width:12.00em;}.yui-t1 #yui-main .yui-b{margin-left:13.30769em;*margin-left:13.05em;}.yui-t2 .yui-b{float:left;width:13.8461em;*width:13.50em;}.yui-t2 #yui-main .yui-b{margin-left:14.8461em;*margin-left:14.55em;}.yui-t3 .yui-b{float:left;width:23.0769em;*width:22.50em;}.yui-t3 #yui-main .yui-b{margin-left:24.0769em;*margin-left:23.62em;}.yui-t4 .yui-b{float:right;width:13.8456em;*width:13.50em;}.yui-t4 #yui-main .yui-b{margin-right:14.8456em;*margin-right:14.55em;}.yui-t5 .yui-b{float:right;width:18.4615em;*width:18.00em;}.yui-t5 #yui-main .yui-b{margin-right:19.4615em;*margin-right:19.125em;}.yui-t6 .yui-b{float:right;width:23.0769em;*width:22.50em;}.yui-t6 #yui-main .yui-b{margin-right:24.0769em;*margin-right:23.62em;}.yui-t7 #yui-main .yui-b{display:block;margin:0 0 1em 0;}#yui-main .yui-b{float:none;width:auto;}.yui-gb .yui-u,.yui-g .yui-gb .yui-u,.yui-gb .yui-g,.yui-gb .yui-gb,.yui-gb .yui-gc,.yui-gb .yui-gd,.yui-gb .yui-ge,.yui-gb .yui-gf,.yui-gc .yui-u,.yui-gc .yui-g,.yui-gd .yui-u{float:left;}.yui-g .yui-u,.yui-g .yui-g,.yui-g .yui-gb,.yui-g .yui-gc,.yui-g .yui-gd,.yui-g .yui-ge,.yui-g .yui-gf,.yui-gc .yui-u,.yui-gd .yui-g,.yui-g .yui-gc .yui-u,.yui-ge .yui-u,.yui-ge .yui-g,.yui-gf .yui-g,.yui-gf .yui-u{float:right;}.yui-g div.first,.yui-gb div.first,.yui-gc div.first,.yui-gd div.first,.yui-ge div.first,.yui-gf div.first,.yui-g .yui-gc div.first,.yui-g .yui-ge div.first,.yui-gc div.first div.first{float:left;}.yui-g .yui-u,.yui-g .yui-g,.yui-g .yui-gb,.yui-g .yui-gc,.yui-g .yui-gd,.yui-g .yui-ge,.yui-g .yui-gf{width:49.1%;}.yui-gb .yui-u,.yui-g .yui-gb .yui-u,.yui-gb .yui-g,.yui-gb .yui-gb,.yui-gb .yui-gc,.yui-gb .yui-gd,.yui-gb .yui-ge,.yui-gb .yui-gf,.yui-gc .yui-u,.yui-gc .yui-g,.yui-gd .yui-u{width:32%;margin-left:1.99%;}.yui-gb .yui-u{*margin-left:1.9%;*width:31.9%;}.yui-gc div.first,.yui-gd .yui-u{width:66%;}.yui-gd div.first{width:32%;}.yui-ge div.first,.yui-gf .yui-u{width:74.2%;}.yui-ge .yui-u,.yui-gf div.first{width:24%;}.yui-g .yui-gb div.first,.yui-gb div.first,.yui-gc div.first,.yui-gd div.first{margin-left:0;}.yui-g .yui-g .yui-u,.yui-gb .yui-g .yui-u,.yui-gc .yui-g .yui-u,.yui-gd .yui-g .yui-u,.yui-ge .yui-g .yui-u,.yui-gf .yui-g .yui-u{width:49%;*width:48.1%;*margin-left:0;}.yui-g .yui-gb div.first,.yui-gb .yui-gb div.first{*margin-right:0;*width:32%;_width:31.7%;}.yui-g .yui-gc div.first,.yui-gd .yui-g{width:66%;}.yui-gb .yui-g div.first{*margin-right:4%;_margin-right:1.3%;}.yui-gb .yui-gc div.first,.yui-gb .yui-gd div.first{*margin-right:0;}.yui-gb .yui-gb .yui-u,.yui-gb .yui-gc .yui-u{*margin-left:1.8%;_margin-left:4%;}.yui-g .yui-gb .yui-u{_margin-left:1.0%;}.yui-gb .yui-gd .yui-u{*width:66%;_width:61.2%;}.yui-gb .yui-gd div.first{*width:31%;_width:29.5%;}.yui-g .yui-gc .yui-u,.yui-gb .yui-gc .yui-u{width:32%;_float:right;margin-right:0;_margin-left:0;}.yui-gb .yui-gc div.first{width:66%;*float:left;*margin-left:0;}.yui-gb .yui-ge .yui-u,.yui-gb .yui-gf .yui-u{margin:0;}.yui-gb .yui-gb .yui-u{_margin-left:.7%;}.yui-gb .yui-g div.first,.yui-gb .yui-gb div.first{*margin-left:0;}.yui-gc .yui-g .yui-u,.yui-gd .yui-g .yui-u{*width:48.1%;*margin-left:0;}s .yui-gb .yui-gd div.first{width:32%;}.yui-g .yui-gd div.first{_width:29.9%;}.yui-ge .yui-g{width:24%;}.yui-gf .yui-g{width:74.2%;}.yui-gb .yui-ge div.yui-u,.yui-gb .yui-gf div.yui-u{float:right;}.yui-gb .yui-ge div.first,.yui-gb .yui-gf div.first{float:left;}.yui-gb .yui-ge .yui-u,.yui-gb .yui-gf div.first{*width:24%;_width:20%;}.yui-gb .yui-ge div.first,.yui-gb .yui-gf .yui-u{*width:73.5%;_width:65.5%;}.yui-ge div.first .yui-gd .yui-u{width:65%;}.yui-ge div.first .yui-gd div.first{width:32%;}#bd:after,.yui-g:after,.yui-gb:after,.yui-gc:after,.yui-gd:after,.yui-ge:after,.yui-gf:after{content:".";display:block;height:0;clear:both;visibility:hidden;}#bd,.yui-g,.yui-gb,.yui-gc,.yui-gd,.yui-ge,.yui-gf{zoom:1;} -------------------------------------------------------------------------------- /docs/source/_themes/djangodocs/theme.conf: -------------------------------------------------------------------------------- 1 | [theme] 2 | inherit = basic 3 | stylesheet = default.css 4 | pygments_style = trac 5 | -------------------------------------------------------------------------------- /docs/source/admin-vote.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pyexcel-webwares/django-excel/a17fea39ffcbf3b7c0d195cab5002ba8c8d416a9/docs/source/admin-vote.png -------------------------------------------------------------------------------- /docs/source/choice-admin.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pyexcel-webwares/django-excel/a17fea39ffcbf3b7c0d195cab5002ba8c8d416a9/docs/source/choice-admin.png -------------------------------------------------------------------------------- /docs/source/choice-sheet.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pyexcel-webwares/django-excel/a17fea39ffcbf3b7c0d195cab5002ba8c8d416a9/docs/source/choice-sheet.png -------------------------------------------------------------------------------- /docs/source/conf.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | DESCRIPTION = ( 3 | 'A django middleware that provides one application programming interfac' + 4 | 'e to read and write data in different excel file formats' + 5 | '' 6 | ) 7 | extensions = [ 8 | 'sphinx.ext.autodoc', 9 | 'sphinx.ext.doctest', 10 | 'sphinx.ext.intersphinx', 11 | 'sphinx.ext.viewcode', 12 | 'sphinxcontrib.excel' 13 | ] 14 | intersphinx_mapping = { 15 | 'pyexcel': ('http://pyexcel.readthedocs.io/en/latest/', None), 16 | } 17 | templates_path = ['_templates'] 18 | source_suffix = '.rst' 19 | master_doc = 'index' 20 | 21 | project = u'django-excel' 22 | copyright = u'2015-2018 Onni Software Ltd.' 23 | version = '0.0.10' 24 | release = '0.0.10' 25 | exclude_patterns = [] 26 | pygments_style = 'sphinx' 27 | import os # noqa 28 | import sys # noqa 29 | sys.path.append(os.path.abspath('_themes')) 30 | html_theme_path = ['_themes'] 31 | html_theme = 'djangodocs' 32 | html_static_path = ['_static'] 33 | htmlhelp_basename = 'django-exceldoc' 34 | latex_elements = {} 35 | latex_documents = [ 36 | ('index', 'django-excel.tex', 37 | 'django-excel Documentation', 38 | 'Onni Software Ltd.', 'manual'), 39 | ] 40 | man_pages = [ 41 | ('index', 'django-excel', 42 | 'django-excel Documentation', 43 | [u'Onni Software Ltd.'], 1) 44 | ] 45 | texinfo_documents = [ 46 | ('index', 'django-excel', 47 | 'django-excel Documentation', 48 | 'Onni Software Ltd.', 'django-excel', 49 | DESCRIPTION, 50 | 'Miscellaneous'), 51 | ] 52 | -------------------------------------------------------------------------------- /docs/source/contents.rst: -------------------------------------------------------------------------------- 1 | Table of contents 2 | ================================================================================ 3 | 4 | .. toctree:: 5 | 6 | index 7 | 8 | -------------------------------------------------------------------------------- /docs/source/custom-export.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pyexcel-webwares/django-excel/a17fea39ffcbf3b7c0d195cab5002ba8c8d416a9/docs/source/custom-export.png -------------------------------------------------------------------------------- /docs/source/download-dialog.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pyexcel-webwares/django-excel/a17fea39ffcbf3b7c0d195cab5002ba8c8d416a9/docs/source/download-dialog.png -------------------------------------------------------------------------------- /docs/source/download-file.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pyexcel-webwares/django-excel/a17fea39ffcbf3b7c0d195cab5002ba8c8d416a9/docs/source/download-file.png -------------------------------------------------------------------------------- /docs/source/handsontable-choice.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pyexcel-webwares/django-excel/a17fea39ffcbf3b7c0d195cab5002ba8c8d416a9/docs/source/handsontable-choice.png -------------------------------------------------------------------------------- /docs/source/handsontable-embedded.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pyexcel-webwares/django-excel/a17fea39ffcbf3b7c0d195cab5002ba8c8d416a9/docs/source/handsontable-embedded.png -------------------------------------------------------------------------------- /docs/source/handsontable-question.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pyexcel-webwares/django-excel/a17fea39ffcbf3b7c0d195cab5002ba8c8d416a9/docs/source/handsontable-question.png -------------------------------------------------------------------------------- /docs/source/import-page.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pyexcel-webwares/django-excel/a17fea39ffcbf3b7c0d195cab5002ba8c8d416a9/docs/source/import-page.png -------------------------------------------------------------------------------- /docs/source/question-admin.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pyexcel-webwares/django-excel/a17fea39ffcbf3b7c0d195cab5002ba8c8d416a9/docs/source/question-admin.png -------------------------------------------------------------------------------- /docs/source/question-sheet.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pyexcel-webwares/django-excel/a17fea39ffcbf3b7c0d195cab5002ba8c8d416a9/docs/source/question-sheet.png -------------------------------------------------------------------------------- /docs/source/spelling_wordlist.txt: -------------------------------------------------------------------------------- 1 | admin 2 | url 3 | pyexcel 4 | django 5 | tsv 6 | tsvz 7 | xls 8 | xlsx 9 | xlsm 10 | Django 11 | IntelliJ 12 | ide 13 | ods 14 | csvz 15 | orgtbl 16 | mediawiki 17 | pypy 18 | init 19 | json 20 | plugins 21 | plugin 22 | rst 23 | handsontable 24 | xlwt 25 | patreon 26 | un 27 | svg 28 | odfpy 29 | io 30 | -------------------------------------------------------------------------------- /docs/source/survey-result.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pyexcel-webwares/django-excel/a17fea39ffcbf3b7c0d195cab5002ba8c8d416a9/docs/source/survey-result.png -------------------------------------------------------------------------------- /docs/source/upload-form.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pyexcel-webwares/django-excel/a17fea39ffcbf3b7c0d195cab5002ba8c8d416a9/docs/source/upload-form.png -------------------------------------------------------------------------------- /docs/source/votes-handson-table.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pyexcel-webwares/django-excel/a17fea39ffcbf3b7c0d195cab5002ba8c8d416a9/docs/source/votes-handson-table.png -------------------------------------------------------------------------------- /format.sh: -------------------------------------------------------------------------------- 1 | isort $(find django_excel -name "*.py"|xargs echo) $(find tests -name "*.py"|xargs echo) 2 | black -l 79 django_excel 3 | black -l 79 tests 4 | -------------------------------------------------------------------------------- /lint.sh: -------------------------------------------------------------------------------- 1 | pip install flake8 2 | flake8 --exclude=.moban.d,docs,setup.py --ignore=F401,E402,E501,W503 --builtins=unicode,xrange,long . && python setup.py checkdocs -------------------------------------------------------------------------------- /manage.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | import os 3 | import sys 4 | 5 | if __name__ == "__main__": 6 | os.environ.setdefault("DJANGO_SETTINGS_MODULE", "mysite.settings") 7 | 8 | from django.core.management import execute_from_command_line 9 | 10 | execute_from_command_line(sys.argv) 11 | -------------------------------------------------------------------------------- /mysite/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pyexcel-webwares/django-excel/a17fea39ffcbf3b7c0d195cab5002ba8c8d416a9/mysite/__init__.py -------------------------------------------------------------------------------- /mysite/settings.py: -------------------------------------------------------------------------------- 1 | """ 2 | Django settings for mysite project. 3 | 4 | For more information on this file, see 5 | https://docs.djangoproject.com/en/1.7/topics/settings/ 6 | 7 | For the full list of settings and their values, see 8 | https://docs.djangoproject.com/en/1.7/ref/settings/ 9 | """ 10 | 11 | # Build paths inside the project like this: os.path.join(BASE_DIR, ...) 12 | import os 13 | import django 14 | 15 | BASE_DIR = os.path.dirname(os.path.dirname(__file__)) 16 | 17 | 18 | # Quick-start development settings - unsuitable for production 19 | # See https://docs.djangoproject.com/en/1.7/howto/deployment/checklist/ 20 | 21 | # SECURITY WARNING: keep the secret key used in production secret! 22 | SECRET_KEY = '&kzg)(b_xt1*nyp-s97ibt01b7p+67#&-brmb_nqj&dn_%q6tc' 23 | 24 | # SECURITY WARNING: don't run with debug turned on in production! 25 | DEBUG = True 26 | 27 | ALLOWED_HOSTS = [] 28 | 29 | # Application definition 30 | 31 | FILE_UPLOAD_HANDLERS = ("django_excel.ExcelMemoryFileUploadHandler", 32 | "django_excel.TemporaryExcelFileUploadHandler") 33 | 34 | INSTALLED_APPS = ( 35 | 'django.contrib.admin', 36 | 'django.contrib.auth', 37 | 'django.contrib.contenttypes', 38 | 'django.contrib.sessions', 39 | 'django.contrib.messages', 40 | 'django.contrib.staticfiles', 41 | 'polls' 42 | ) 43 | 44 | COMMON_MIDDLEWARE = ( 45 | 'django.contrib.sessions.middleware.SessionMiddleware', 46 | 'django.middleware.common.CommonMiddleware', 47 | 'django.middleware.csrf.CsrfViewMiddleware', 48 | 'django.contrib.auth.middleware.AuthenticationMiddleware', 49 | 'django.contrib.messages.middleware.MessageMiddleware', 50 | 'django.middleware.clickjacking.XFrameOptionsMiddleware' 51 | ) 52 | 53 | if django.VERSION[0] > 1: 54 | MIDDLEWARE = ( 55 | 'django.middleware.security.SecurityMiddleware', 56 | *COMMON_MIDDLEWARE 57 | ) 58 | 59 | else: 60 | MIDDLEWARE_CLASSES = COMMON_MIDDLEWARE 61 | 62 | 63 | ROOT_URLCONF = 'mysite.urls' 64 | 65 | WSGI_APPLICATION = 'mysite.wsgi.application' 66 | 67 | 68 | # Database 69 | # https://docs.djangoproject.com/en/1.7/ref/settings/#databases 70 | 71 | DATABASES = { 72 | 'default': { 73 | 'ENGINE': 'django.db.backends.sqlite3', 74 | 'NAME': os.path.join(BASE_DIR, 'db.sqlite3'), 75 | } 76 | } 77 | 78 | TEMPLATES = [ 79 | { 80 | 'BACKEND': 'django.template.backends.django.DjangoTemplates', 81 | 'DIRS': [os.path.join(BASE_DIR, 'templates')], 82 | 'APP_DIRS': True, 83 | 'OPTIONS': { 84 | 'context_processors': [ 85 | 'django.template.context_processors.debug', 86 | 'django.template.context_processors.request', 87 | 'django.contrib.auth.context_processors.auth', 88 | 'django.contrib.messages.context_processors.messages', 89 | ], 90 | } 91 | }, 92 | ] 93 | 94 | 95 | # Internationalization 96 | # https://docs.djangoproject.com/en/1.7/topics/i18n/ 97 | 98 | LANGUAGE_CODE = 'en-us' 99 | 100 | TIME_ZONE = 'UTC' 101 | 102 | USE_I18N = True 103 | 104 | USE_L10N = True 105 | 106 | USE_TZ = True 107 | 108 | 109 | # Static files (CSS, JavaScript, Images) 110 | # https://docs.djangoproject.com/en/1.7/howto/static-files/ 111 | 112 | STATIC_URL = '/static/' 113 | -------------------------------------------------------------------------------- /mysite/urls.py: -------------------------------------------------------------------------------- 1 | from django.conf.urls import include 2 | from django.urls import re_path 3 | from django.contrib import admin 4 | 5 | 6 | urlpatterns = [ 7 | re_path(r'^admin/', admin.site.urls), 8 | re_path(r'^polls/', include('polls.urls')) 9 | ] 10 | -------------------------------------------------------------------------------- /mysite/wsgi.py: -------------------------------------------------------------------------------- 1 | """ 2 | WSGI config for mysite project. 3 | 4 | It exposes the WSGI callable as a module-level variable named ``application``. 5 | 6 | For more information on this file, see 7 | https://docs.djangoproject.com/en/1.7/howto/deployment/wsgi/ 8 | """ 9 | import os 10 | from django.core.wsgi import get_wsgi_application 11 | 12 | os.environ.setdefault("DJANGO_SETTINGS_MODULE", "mysite.settings") 13 | application = get_wsgi_application() 14 | -------------------------------------------------------------------------------- /polls/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pyexcel-webwares/django-excel/a17fea39ffcbf3b7c0d195cab5002ba8c8d416a9/polls/__init__.py -------------------------------------------------------------------------------- /polls/admin.py: -------------------------------------------------------------------------------- 1 | from django.contrib import admin 2 | from polls.models import Question, Choice 3 | 4 | # Register your models here. 5 | admin.site.register(Question) 6 | admin.site.register(Choice) 7 | -------------------------------------------------------------------------------- /polls/models.py: -------------------------------------------------------------------------------- 1 | from django.db import models 2 | 3 | 4 | class Question(models.Model): 5 | question_text = models.CharField(max_length=200) 6 | pub_date = models.DateTimeField('date published') 7 | slug = models.CharField(max_length=10, unique=True, 8 | default="question") 9 | 10 | def __str__(self): 11 | return self.question_text 12 | 13 | 14 | class Choice(models.Model): 15 | question = models.ForeignKey(Question, on_delete=models.CASCADE) 16 | choice_text = models.CharField(max_length=200) 17 | votes = models.IntegerField(default=0) 18 | 19 | def __str__(self): 20 | return self.choice_text 21 | -------------------------------------------------------------------------------- /polls/urls.py: -------------------------------------------------------------------------------- 1 | from django.urls import re_path 2 | from polls import views 3 | 4 | urlpatterns = [ 5 | re_path(r'^$', views.upload, name='uplink'), 6 | re_path(r'^download/(.*)', views.download, name="download"), 7 | re_path(r'^download_attachment/(.*)/(.*)', views.download_as_attachment, 8 | name="download_attachment"), 9 | re_path(r'^exchange/(.*)', views.exchange, name="exchange"), 10 | re_path(r'^parse/(.*)', views.parse, name="parse"), 11 | re_path(r'^import/', views.import_data, name="import"), 12 | re_path(r'^import_sheet/', views.import_sheet, name="import_sheet"), 13 | re_path(r'^export/(.*)', views.export_data, name="export"), 14 | re_path(r'^handson_view/', views.handson_table, name="handson_view"), 15 | 16 | # handson table view 17 | re_path(r'^embedded_handson_view/', 18 | views.embed_handson_table, name="embed_handson_view"), 19 | re_path(r'^embedded_handson_view_single/', 20 | views.embed_handson_table_from_a_single_table, 21 | name="embed_handson_view"), 22 | # survey_result 23 | re_path('^survey_result/', 24 | views.survey_result, name='survey_result'), 25 | 26 | # testing purpose 27 | re_path(r'^import_using_isave/', 28 | views.import_data_using_isave_book_as), 29 | re_path(r'^import_sheet_using_isave/', 30 | views.import_sheet_using_isave_to_database), 31 | re_path(r'^import_without_bulk_save/', 32 | views.import_without_bulk_save, name="import_no_bulk_save") 33 | ] 34 | -------------------------------------------------------------------------------- /polls/views.py: -------------------------------------------------------------------------------- 1 | from django.shortcuts import render, redirect 2 | from django.http import HttpResponseBadRequest, HttpResponse 3 | from _compact import JsonResponse 4 | from django import forms 5 | import django_excel as excel 6 | from polls.models import Question, Choice 7 | 8 | data = [[1, 2, 3], [4, 5, 6]] 9 | 10 | 11 | class UploadFileForm(forms.Form): 12 | file = forms.FileField() 13 | 14 | 15 | # Create your views here. 16 | def upload(request): 17 | if request.method == "POST": 18 | form = UploadFileForm(request.POST, request.FILES) 19 | if form.is_valid(): 20 | filehandle = request.FILES["file"] 21 | return excel.make_response( 22 | filehandle.get_sheet(), "csv", file_name="download" 23 | ) 24 | else: 25 | form = UploadFileForm() 26 | return render( 27 | request, 28 | "upload_form.html", 29 | { 30 | "form": form, 31 | "title": "Excel file upload and download example", 32 | "header": ( 33 | "Please choose any excel file " 34 | + "from your cloned repository:" 35 | ), 36 | }, 37 | ) 38 | 39 | 40 | def download(request, file_type): 41 | sheet = excel.pe.Sheet(data) 42 | return excel.make_response(sheet, file_type) 43 | 44 | 45 | def download_as_attachment(request, file_type, file_name): 46 | return excel.make_response_from_array(data, file_type, file_name=file_name) 47 | 48 | 49 | def export_data(request, atype): 50 | if atype == "sheet": 51 | return excel.make_response_from_a_table( 52 | Question, "xls", file_name="sheet" 53 | ) 54 | elif atype == "book": 55 | return excel.make_response_from_tables( 56 | [Question, Choice], "xls", file_name="book" 57 | ) 58 | elif atype == "custom": 59 | question = Question.objects.get(slug="ide") 60 | query_sets = Choice.objects.filter(question=question) 61 | column_names = ["choice_text", "id", "votes"] 62 | return excel.make_response_from_query_sets( 63 | query_sets, column_names, "xls", file_name="custom" 64 | ) 65 | else: 66 | return HttpResponseBadRequest( 67 | "Bad request. please put one of these " 68 | + "in your url suffix: sheet, book or custom" 69 | ) 70 | 71 | 72 | def import_data(request): 73 | if request.method == "POST": 74 | form = UploadFileForm(request.POST, request.FILES) 75 | 76 | def choice_func(row): 77 | q = Question.objects.filter(slug=row[0])[0] 78 | row[0] = q 79 | return row 80 | 81 | if form.is_valid(): 82 | request.FILES["file"].save_book_to_database( 83 | models=[Question, Choice], 84 | initializers=[None, choice_func], 85 | mapdicts=[ 86 | ["question_text", "pub_date", "slug"], 87 | {"Question": "question", "Choice": "choice_text", "Votes": "votes"}, 88 | ], 89 | ) 90 | return redirect("handson_view") 91 | else: 92 | return HttpResponseBadRequest() 93 | else: 94 | form = UploadFileForm() 95 | return render( 96 | request, 97 | "upload_form.html", 98 | { 99 | "form": form, 100 | "title": "Import excel data into database example", 101 | "header": "Please upload sample-data.xls:", 102 | }, 103 | ) 104 | 105 | 106 | def import_sheet(request): 107 | if request.method == "POST": 108 | form = UploadFileForm(request.POST, request.FILES) 109 | if form.is_valid(): 110 | request.FILES["file"].save_to_database( 111 | name_columns_by_row=2, 112 | model=Question, 113 | mapdict=["question_text", "pub_date", "slug"], 114 | ) 115 | return HttpResponse("OK") 116 | else: 117 | return HttpResponseBadRequest() 118 | else: 119 | form = UploadFileForm() 120 | return render(request, "upload_form.html", {"form": form}) 121 | 122 | 123 | def exchange(request, file_type): 124 | form = UploadFileForm(request.POST, request.FILES) 125 | if form.is_valid(): 126 | filehandle = request.FILES["file"] 127 | return excel.make_response(filehandle.get_sheet(), file_type) 128 | else: 129 | return HttpResponseBadRequest() 130 | 131 | 132 | def parse(request, data_struct_type): 133 | form = UploadFileForm(request.POST, request.FILES) 134 | if form.is_valid(): 135 | filehandle = request.FILES["file"] 136 | if data_struct_type == "array": 137 | return JsonResponse({"result": filehandle.get_array()}) 138 | elif data_struct_type == "dict": 139 | return JsonResponse(filehandle.get_dict()) 140 | elif data_struct_type == "records": 141 | return JsonResponse({"result": filehandle.get_records()}) 142 | elif data_struct_type == "book": 143 | return JsonResponse(filehandle.get_book().to_dict()) 144 | elif data_struct_type == "book_dict": 145 | return JsonResponse(filehandle.get_book_dict()) 146 | else: 147 | return HttpResponseBadRequest() 148 | else: 149 | return HttpResponseBadRequest() 150 | 151 | 152 | def handson_table(request): 153 | return excel.make_response_from_tables( 154 | [Question, Choice], "handsontable.html" 155 | ) 156 | 157 | 158 | def embed_handson_table(request): 159 | """ 160 | Renders two table in a handsontable 161 | """ 162 | content = excel.pe.save_book_as( 163 | models=[Question, Choice], 164 | dest_file_type="handsontable.html", 165 | dest_embed=True, 166 | ) 167 | content.seek(0) 168 | return render( 169 | request, 170 | "custom-handson-table.html", 171 | {"handsontable_content": content.read()}, 172 | ) 173 | 174 | 175 | def embed_handson_table_from_a_single_table(request): 176 | """ 177 | Renders one table in a handsontable 178 | """ 179 | content = excel.pe.save_as( 180 | model=Question, dest_file_type="handsontable.html", dest_embed=True 181 | ) 182 | content.seek(0) 183 | return render( 184 | request, 185 | "custom-handson-table.html", 186 | {"handsontable_content": content.read()}, 187 | ) 188 | 189 | 190 | def survey_result(request): 191 | question = Question.objects.get(slug="ide") 192 | query_sets = Choice.objects.filter(question=question) 193 | column_names = ["choice_text", "votes"] 194 | 195 | # Obtain a pyexcel sheet from the query sets 196 | sheet = excel.pe.get_sheet( 197 | query_sets=query_sets, column_names=column_names 198 | ) 199 | sheet.name_columns_by_row(0) 200 | sheet.column.format("votes", int) 201 | 202 | # Transform the sheet into an svg chart 203 | svg = excel.pe.save_as( 204 | array=[sheet.column["choice_text"], sheet.column["votes"]], 205 | dest_file_type="svg", 206 | dest_chart_type="pie", 207 | dest_title=question.question_text, 208 | dest_width=600, 209 | dest_height=400, 210 | ) 211 | 212 | return render(request, "survey_result.html", dict(svg=svg.read())) 213 | 214 | 215 | def import_sheet_using_isave_to_database(request): 216 | if request.method == "POST": 217 | form = UploadFileForm(request.POST, request.FILES) 218 | if form.is_valid(): 219 | request.FILES["file"].isave_to_database( 220 | model=Question, mapdict=["question_text", "pub_date", "slug"] 221 | ) 222 | return HttpResponse("OK") 223 | else: 224 | return HttpResponseBadRequest() 225 | else: 226 | form = UploadFileForm() 227 | return render(request, "upload_form.html", {"form": form}) 228 | 229 | 230 | def import_data_using_isave_book_as(request): 231 | if request.method == "POST": 232 | form = UploadFileForm(request.POST, request.FILES) 233 | 234 | def choice_func(row): 235 | q = Question.objects.filter(slug=row[0])[0] 236 | row[0] = q 237 | return row 238 | 239 | if form.is_valid(): 240 | request.FILES["file"].isave_book_to_database( 241 | models=[Question, Choice], 242 | initializers=[None, choice_func], 243 | mapdicts=[ 244 | ["question_text", "pub_date", "slug"], 245 | {"Question": "question", "Choice": "choice_text", "Votes": "votes"}, 246 | ], 247 | ) 248 | return redirect("handson_view") 249 | else: 250 | return HttpResponseBadRequest() 251 | else: 252 | form = UploadFileForm() 253 | return render( 254 | request, 255 | "upload_form.html", 256 | { 257 | "form": form, 258 | "title": "Import excel data into database example", 259 | "header": "Please upload sample-data.xls:", 260 | }, 261 | ) 262 | 263 | 264 | def import_without_bulk_save(request): 265 | if request.method == "POST": 266 | form = UploadFileForm(request.POST, request.FILES) 267 | 268 | def choice_func(row): 269 | q = Question.objects.filter(slug=row[0])[0] 270 | row[0] = q 271 | return row 272 | 273 | if form.is_valid(): 274 | request.FILES["file"].save_book_to_database( 275 | models=[Question, Choice], 276 | initializers=[None, choice_func], 277 | mapdicts=[ 278 | ["question_text", "pub_date", "slug"], 279 | ["question", "choice_text", "votes"], 280 | ], 281 | bulk_save=False, 282 | ) 283 | return redirect("handson_view") 284 | else: 285 | return HttpResponseBadRequest() 286 | else: 287 | form = UploadFileForm() 288 | return render( 289 | request, 290 | "upload_form.html", 291 | { 292 | "form": form, 293 | "title": "Import excel data into database example", 294 | "header": "Please upload sample-data.xls:", 295 | }, 296 | ) 297 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | pyexcel>=0.5.7 2 | pyexcel-webio>=0.1.2 3 | Django>=1.6.1 4 | -------------------------------------------------------------------------------- /rnd_requirements.txt: -------------------------------------------------------------------------------- 1 | https://github.com/pyexcel/pyexcel-io/archive/dev.zip 2 | -------------------------------------------------------------------------------- /sample-data.xls: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pyexcel-webwares/django-excel/a17fea39ffcbf3b7c0d195cab5002ba8c8d416a9/sample-data.xls -------------------------------------------------------------------------------- /sample-sheet-for-isave.xls: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pyexcel-webwares/django-excel/a17fea39ffcbf3b7c0d195cab5002ba8c8d416a9/sample-sheet-for-isave.xls -------------------------------------------------------------------------------- /sample-sheet.xls: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pyexcel-webwares/django-excel/a17fea39ffcbf3b7c0d195cab5002ba8c8d416a9/sample-sheet.xls -------------------------------------------------------------------------------- /setup.cfg: -------------------------------------------------------------------------------- 1 | [metadata] 2 | description-file = README.rst 3 | [bdist_wheel] 4 | universal = 1 5 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | """ 4 | Template by pypi-mobans 5 | """ 6 | 7 | import os 8 | import sys 9 | import codecs 10 | import locale 11 | import platform 12 | from shutil import rmtree 13 | 14 | from setuptools import Command, setup, find_packages 15 | 16 | PY2 = sys.version_info[0] == 2 17 | PY26 = PY2 and sys.version_info[1] < 7 18 | PY33 = sys.version_info < (3, 4) 19 | 20 | # Work around mbcs bug in distutils. 21 | # http://bugs.python.org/issue10945 22 | # This work around is only if a project supports Python < 3.4 23 | 24 | # Work around for locale not being set 25 | try: 26 | lc = locale.getlocale() 27 | pf = platform.system() 28 | if pf != "Windows" and lc == (None, None): 29 | locale.setlocale(locale.LC_ALL, "C.UTF-8") 30 | except (ValueError, UnicodeError, locale.Error): 31 | locale.setlocale(locale.LC_ALL, "en_US.UTF-8") 32 | 33 | NAME = "django-excel" 34 | AUTHOR = "C.W." 35 | VERSION = "0.0.10" 36 | EMAIL = "info@pyexcel.org" 37 | LICENSE = "New BSD" 38 | DESCRIPTION = ( 39 | "A django middleware that provides one application programming" + 40 | "interface to read and write data in different excel file formats" 41 | ) 42 | URL = "https://github.com/pyexcel-webwares/django-excel" 43 | DOWNLOAD_URL = "%s/archive/0.0.10.tar.gz" % URL 44 | FILES = ["README.rst", "CONTRIBUTORS.rst", "CHANGELOG.rst"] 45 | KEYWORDS = [ 46 | "python", 47 | "API", 48 | "Django", 49 | ] 50 | 51 | CLASSIFIERS = [ 52 | "Topic :: Software Development :: Libraries", 53 | "Programming Language :: Python", 54 | "Intended Audience :: Developers", 55 | "Programming Language :: Python :: 2.6", 56 | "Programming Language :: Python :: 2.7", 57 | "Programming Language :: Python :: 3.3", 58 | "Programming Language :: Python :: 3.4", 59 | "Programming Language :: Python :: 3.5", 60 | "Programming Language :: Python :: 3.6", 61 | "Programming Language :: Python :: 3.7", 62 | "Programming Language :: Python :: 3.8", 63 | 64 | 'License :: OSI Approved :: BSD License', 65 | 'Development Status :: 3 - Alpha', 66 | 'Environment :: Web Environment', 67 | 'Topic :: Internet :: WWW/HTTP', 68 | 'Topic :: Software Development :: Libraries :: Python Modules', 69 | 'Framework :: Django :: 1.7', 70 | 'Framework :: Django :: 1.8', 71 | 'Framework :: Django :: 1.9', 72 | 'Programming Language :: Python :: 2', 73 | 'Programming Language :: Python :: 2.7', 74 | 'Programming Language :: Python :: 3', 75 | 'Programming Language :: Python :: 3.3', 76 | 'Programming Language :: Python :: 3.4', 77 | 'Programming Language :: Python :: 3.5' 78 | 79 | ] 80 | 81 | 82 | INSTALL_REQUIRES = [ 83 | "pyexcel>=0.5.7", 84 | "pyexcel-webio>=0.1.2", 85 | "Django>=1.6.1", 86 | ] 87 | SETUP_COMMANDS = {} 88 | 89 | PACKAGES = find_packages(exclude=["ez_setup", "examples", "tests", "tests.*"]) 90 | EXTRAS_REQUIRE = { 91 | "xls": ['pyexcel-xls>=0.4.0'], 92 | "xlsx": ['pyexcel-xlsx>=0.4.0'], 93 | "ods": ['pyexcel-ods3>=0.4.0'], 94 | } 95 | # You do not need to read beyond this line 96 | PUBLISH_COMMAND = "{0} setup.py sdist bdist_wheel upload -r pypi".format(sys.executable) 97 | HERE = os.path.abspath(os.path.dirname(__file__)) 98 | 99 | GS_COMMAND = ("gease django-excel v0.0.10 " + 100 | "Find 0.0.10 in changelog for more details") 101 | NO_GS_MESSAGE = ("Automatic github release is disabled. " + 102 | "Please install gease to enable it.") 103 | UPLOAD_FAILED_MSG = ( 104 | 'Upload failed. please run "%s" yourself.' % PUBLISH_COMMAND) 105 | 106 | 107 | class PublishCommand(Command): 108 | """Support setup.py upload.""" 109 | 110 | description = "Build and publish the package on github and pypi" 111 | user_options = [] 112 | 113 | @staticmethod 114 | def status(s): 115 | """Prints things in bold.""" 116 | print("\033[1m{0}\033[0m".format(s)) 117 | 118 | def initialize_options(self): 119 | pass 120 | 121 | def finalize_options(self): 122 | pass 123 | 124 | def run(self): 125 | try: 126 | self.status("Removing previous builds...") 127 | rmtree(os.path.join(HERE, "dist")) 128 | rmtree(os.path.join(HERE, "build")) 129 | rmtree(os.path.join(HERE, "django_excel.egg-info")) 130 | except OSError: 131 | pass 132 | 133 | self.status("Building Source and Wheel (universal) distribution...") 134 | run_status = True 135 | if has_gease(): 136 | run_status = os.system(GS_COMMAND) == 0 137 | else: 138 | self.status(NO_GS_MESSAGE) 139 | if run_status: 140 | if os.system(PUBLISH_COMMAND) != 0: 141 | self.status(UPLOAD_FAILED_MSG) 142 | 143 | sys.exit() 144 | 145 | 146 | SETUP_COMMANDS.update({ 147 | "publish": PublishCommand 148 | }) 149 | 150 | def has_gease(): 151 | """ 152 | test if github release command is installed 153 | 154 | visit http://github.com/moremoban/gease for more info 155 | """ 156 | try: 157 | import gease # noqa 158 | return True 159 | except ImportError: 160 | return False 161 | 162 | 163 | def read_files(*files): 164 | """Read files into setup""" 165 | text = "" 166 | for single_file in files: 167 | content = read(single_file) 168 | text = text + content + "\n" 169 | return text 170 | 171 | 172 | def read(afile): 173 | """Read a file into setup""" 174 | the_relative_file = os.path.join(HERE, afile) 175 | with codecs.open(the_relative_file, "r", "utf-8") as opened_file: 176 | content = filter_out_test_code(opened_file) 177 | content = "".join(list(content)) 178 | return content 179 | 180 | 181 | def filter_out_test_code(file_handle): 182 | found_test_code = False 183 | for line in file_handle.readlines(): 184 | if line.startswith(".. testcode:"): 185 | found_test_code = True 186 | continue 187 | if found_test_code is True: 188 | if line.startswith(" "): 189 | continue 190 | else: 191 | empty_line = line.strip() 192 | if len(empty_line) == 0: 193 | continue 194 | else: 195 | found_test_code = False 196 | yield line 197 | else: 198 | for keyword in ["|version|", "|today|"]: 199 | if keyword in line: 200 | break 201 | else: 202 | yield line 203 | 204 | 205 | if __name__ == "__main__": 206 | setup( 207 | test_suite="tests", 208 | name=NAME, 209 | author=AUTHOR, 210 | version=VERSION, 211 | author_email=EMAIL, 212 | description=DESCRIPTION, 213 | url=URL, 214 | download_url=DOWNLOAD_URL, 215 | long_description=read_files(*FILES), 216 | license=LICENSE, 217 | keywords=KEYWORDS, 218 | extras_require=EXTRAS_REQUIRE, 219 | tests_require=["nose"], 220 | install_requires=INSTALL_REQUIRES, 221 | packages=PACKAGES, 222 | include_package_data=True, 223 | zip_safe=False, 224 | classifiers=CLASSIFIERS, 225 | cmdclass=SETUP_COMMANDS 226 | ) 227 | -------------------------------------------------------------------------------- /templates/custom-handson-table.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 7 | 8 |

Handsontable was embedded in a html table

9 | 10 | 11 | 14 | 17 | 18 |
12 | {{handsontable_content|safe}} 13 | 15 |

Hei, this view demonstrate how handsontable can be embedded

16 |
19 | 20 | 21 | -------------------------------------------------------------------------------- /templates/handsontable.html: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pyexcel-webwares/django-excel/a17fea39ffcbf3b7c0d195cab5002ba8c8d416a9/templates/handsontable.html -------------------------------------------------------------------------------- /templates/survey_result.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | {{svg|safe}} 4 | 5 | 6 | -------------------------------------------------------------------------------- /templates/upload_form.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | {{title}} 4 | 5 | 6 |

{{header}}

7 | 8 | {% if form.errors %} 9 |

10 | Please correct the error{{ form.errors|pluralize }} below. 11 |

12 | {% endif %} 13 | 14 |
15 | 16 | {{ form.as_table }} 17 |
18 | {% csrf_token %} 19 | 20 |
21 | 22 | 23 | -------------------------------------------------------------------------------- /test.bat: -------------------------------------------------------------------------------- 1 | pip freeze 2 | nosetests --with-coverage --cover-package django_excel --cover-package tests tests --with-doctest --doctest-extension=.rst README.rst docs/source django_excel 3 | -------------------------------------------------------------------------------- /test.sh: -------------------------------------------------------------------------------- 1 | #/bin/bash 2 | pip freeze 3 | coverage run --source='django_excel' manage.py test 4 | -------------------------------------------------------------------------------- /testResponse.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | import os 4 | import sys 5 | import json 6 | from textwrap import dedent 7 | from polls.models import Question, Choice 8 | from django.test import Client, TestCase 9 | from django.test.utils import override_settings 10 | import pyexcel as pe 11 | from django_excel._compact import urllib_quote 12 | from django_excel import ExcelInMemoryUploadedFile 13 | from nose.tools import eq_ 14 | 15 | PY2 = sys.version_info[0] == 2 16 | if sys.version_info[0] == 2 and sys.version_info[1] < 7: 17 | from ordereddict import OrderedDict 18 | else: 19 | from collections import OrderedDict 20 | 21 | if PY2: 22 | from StringIO import StringIO 23 | else: 24 | from io import StringIO 25 | 26 | 27 | _XLSX_MIME = "application/" + "vnd.openxmlformats-officedocument.spreadsheetml.sheet" 28 | 29 | FILE_TYPE_MIME_TABLE = { 30 | "csv": "text/csv", 31 | "tsv": "text/tab-separated-values", 32 | "csvz": "application/zip", 33 | "tsvz": "application/zip", 34 | "ods": "application/vnd.oasis.opendocument.spreadsheet", 35 | "xls": "application/vnd.ms-excel", 36 | "xlsx": _XLSX_MIME, 37 | "xlsm": "application/vnd.ms-excel.sheet.macroenabled.12", 38 | } 39 | 40 | 41 | class ExcelResponseTestCase(TestCase): 42 | def setUp(self): 43 | self.client = Client() 44 | self.data = [[1, 2, 3], [4, 5, 6]] 45 | self.single_sheet = [["X", "Y", "Z"], [1, 2, 3], [4, 5, 6]] 46 | self.book_content = OrderedDict() 47 | self.book_content.update({"Sheet1": [[1, 1, 1, 1], [2, 2, 2, 2], [3, 3, 3, 3]]}) 48 | self.book_content.update({"Sheet2": [[4, 4, 4, 4], [5, 5, 5, 5], [6, 6, 6, 6]]}) 49 | self.book_content.update( 50 | {"Sheet3": [[u"X", u"Y", u"Z"], [1, 4, 7], [2, 5, 8], [3, 6, 9]]} 51 | ) 52 | 53 | def test_download(self): 54 | for file_type in FILE_TYPE_MIME_TABLE.keys(): 55 | response = self.client.get("/polls/download/" + file_type) 56 | assert response["Content-Type"] == FILE_TYPE_MIME_TABLE[file_type] 57 | sheet = pe.get_sheet(file_type=file_type, file_content=response.content) 58 | sheet.format(int) 59 | array = sheet.to_array() 60 | assert array == self.data 61 | 62 | def test_download_attachment_with_ascii_name(self): 63 | test_file_name = "test" 64 | self._download_and_verify_file_name(test_file_name) 65 | 66 | def test_download_attachment_with_unicode_name(self): 67 | test_file_name = u"中文文件名" 68 | self._download_and_verify_file_name(test_file_name.encode("utf-8")) 69 | 70 | def test_download_attachment_with_unicode_name_as_string(self): 71 | test_file_name = "中文文件名" 72 | self._download_and_verify_file_name(test_file_name) 73 | 74 | def _download_and_verify_file_name(self, file_name): 75 | for file_type in FILE_TYPE_MIME_TABLE.keys(): 76 | url_encoded_file_name = urllib_quote(file_name) 77 | response = self.client.get( 78 | ( 79 | "/polls/download_attachment/%s/%s" 80 | % (file_type, url_encoded_file_name) 81 | ) 82 | ) 83 | assert response["Content-Type"] == FILE_TYPE_MIME_TABLE[file_type] 84 | assert response["Content-Disposition"] == ( 85 | "attachment; filename=%s.%s;filename*=utf-8''%s.%s" 86 | % (url_encoded_file_name, file_type, url_encoded_file_name, file_type) 87 | ) 88 | sheet = pe.get_sheet(file_type=file_type, file_content=response.content) 89 | sheet.format(int) 90 | array = sheet.to_array() 91 | assert array == self.data 92 | 93 | def test_parse_single_sheet(self): 94 | test_sample = { 95 | "array": { 96 | u"result": [[u"X", u"Y", u"Z"], [1.0, 2.0, 3.0], [4.0, 5.0, 6.0]] 97 | }, 98 | "dict": {u"Y": [2.0, 5.0], u"X": [1.0, 4.0], u"Z": [3.0, 6.0]}, 99 | "records": { 100 | u"result": [ 101 | {u"Y": 2.0, u"X": 1.0, u"Z": 3.0}, 102 | {u"Y": 5.0, u"X": 4.0, u"Z": 6.0}, 103 | ] 104 | }, 105 | } 106 | for data_struct_type in test_sample.keys(): 107 | sheet = pe.Sheet(self.single_sheet) 108 | tmp_filename = "test.file.xls" 109 | sheet.save_as(tmp_filename) 110 | with open(tmp_filename, "rb") as fp: 111 | response = self.client.post( 112 | "/polls/parse/" + data_struct_type, data={"file": fp} 113 | ) 114 | self.assertEqual( 115 | json.loads(response.content.decode("utf-8")), 116 | test_sample[data_struct_type], 117 | ) 118 | os.unlink(tmp_filename) 119 | 120 | def test_parse_book(self): 121 | test_sample = ["book", "book_dict"] 122 | expected_dict = { 123 | u"Sheet1": [ 124 | [1.0, 1.0, 1.0, 1.0], 125 | [2.0, 2.0, 2.0, 2.0], 126 | [3.0, 3.0, 3.0, 3.0], 127 | ], 128 | u"Sheet3": [ 129 | [u"X", u"Y", u"Z"], 130 | [1.0, 4.0, 7.0], 131 | [2.0, 5.0, 8.0], 132 | [3.0, 6.0, 9.0], 133 | ], 134 | u"Sheet2": [ 135 | [4.0, 4.0, 4.0, 4.0], 136 | [5.0, 5.0, 5.0, 5.0], 137 | [6.0, 6.0, 6.0, 6.0], 138 | ], 139 | } 140 | for data_struct_type in test_sample: 141 | sheet = pe.Book(self.book_content) 142 | tmp_filename = "test.xls" 143 | sheet.save_as(tmp_filename) 144 | with open(tmp_filename, "rb") as fp: 145 | response = self.client.post( 146 | "/polls/parse/" + data_struct_type, data={"file": fp} 147 | ) 148 | self.assertEqual( 149 | json.loads(response.content.decode("utf-8")), expected_dict 150 | ) 151 | os.unlink(tmp_filename) 152 | 153 | def test_exchange(self): 154 | for in_file_type in FILE_TYPE_MIME_TABLE.keys(): 155 | sheet = pe.Sheet(self.data) 156 | tmp_filename = "test.%s" % in_file_type 157 | sheet.save_as(tmp_filename) 158 | for file_type in FILE_TYPE_MIME_TABLE.keys(): 159 | with open(tmp_filename, "rb") as fp: 160 | response = self.client.post( 161 | "/polls/exchange/" + file_type, data={"file": fp} 162 | ) 163 | self.assertEqual( 164 | response["Content-Type"], FILE_TYPE_MIME_TABLE[file_type] 165 | ) 166 | sheet = pe.get_sheet( 167 | file_type=file_type, file_content=response.content 168 | ) 169 | sheet.format(int) 170 | array = sheet.to_array() 171 | assert array == self.data 172 | os.unlink(tmp_filename) 173 | 174 | 175 | class DatabaseOperationsTestCase(TestCase): 176 | def setUp(self): 177 | self.testfile = "sample-data.xls" 178 | self.maxDiff = None 179 | Question.objects.all().delete() 180 | Choice.objects.all().delete() 181 | 182 | def testBook(self): 183 | fp = open(self.testfile, "rb") 184 | response = self.client.post("/polls/import/", data={"file": fp}) 185 | eq_(response.status_code, 302) 186 | response2 = self.client.get("/polls/export/book") 187 | assert response2.status_code == 200 188 | book = pe.get_book(file_type="xls", file_content=response2.content) 189 | content = dedent( 190 | """ 191 | question: 192 | +----+---------------------------+----------------------------------------------+----------+ 193 | | id | pub_date | question_text | slug | 194 | +----+---------------------------+----------------------------------------------+----------+ 195 | | 1 | 2015-01-28T00:00:00+00:00 | What is your favourite programming language? | language | 196 | +----+---------------------------+----------------------------------------------+----------+ 197 | | 2 | 2015-01-29T00:00:00+00:00 | What is your favourite IDE? | ide | 198 | +----+---------------------------+----------------------------------------------+----------+ 199 | choice: 200 | +---------------+----+-------------+-------+ 201 | | choice_text | id | question_id | votes | 202 | +---------------+----+-------------+-------+ 203 | | Java | 1 | 1 | 0 | 204 | +---------------+----+-------------+-------+ 205 | | C++ | 2 | 1 | 0 | 206 | +---------------+----+-------------+-------+ 207 | | C | 3 | 1 | 0 | 208 | +---------------+----+-------------+-------+ 209 | | Eclipse | 4 | 2 | 0 | 210 | +---------------+----+-------------+-------+ 211 | | Visual Studio | 5 | 2 | 0 | 212 | +---------------+----+-------------+-------+ 213 | | PyCharm | 6 | 2 | 0 | 214 | +---------------+----+-------------+-------+ 215 | | IntelliJ | 7 | 2 | 0 | 216 | +---------------+----+-------------+-------+""" 217 | ).strip( 218 | "\n" 219 | ) # noqa 220 | self.assertEqual(str(book), content) 221 | 222 | def testBookWithoutBulkSave(self): 223 | fp = open(self.testfile, "rb") 224 | response = self.client.post("/polls/import/", data={"file": fp}) 225 | eq_(response.status_code, 302) 226 | response2 = self.client.get("/polls/export/book") 227 | assert response2.status_code == 200 228 | book = pe.get_book(file_type="xls", file_content=response2.content) 229 | content = dedent( 230 | """ 231 | question: 232 | +----+---------------------------+----------------------------------------------+----------+ 233 | | id | pub_date | question_text | slug | 234 | +----+---------------------------+----------------------------------------------+----------+ 235 | | 1 | 2015-01-28T00:00:00+00:00 | What is your favourite programming language? | language | 236 | +----+---------------------------+----------------------------------------------+----------+ 237 | | 2 | 2015-01-29T00:00:00+00:00 | What is your favourite IDE? | ide | 238 | +----+---------------------------+----------------------------------------------+----------+ 239 | choice: 240 | +---------------+----+-------------+-------+ 241 | | choice_text | id | question_id | votes | 242 | +---------------+----+-------------+-------+ 243 | | Java | 1 | 1 | 0 | 244 | +---------------+----+-------------+-------+ 245 | | C++ | 2 | 1 | 0 | 246 | +---------------+----+-------------+-------+ 247 | | C | 3 | 1 | 0 | 248 | +---------------+----+-------------+-------+ 249 | | Eclipse | 4 | 2 | 0 | 250 | +---------------+----+-------------+-------+ 251 | | Visual Studio | 5 | 2 | 0 | 252 | +---------------+----+-------------+-------+ 253 | | PyCharm | 6 | 2 | 0 | 254 | +---------------+----+-------------+-------+ 255 | | IntelliJ | 7 | 2 | 0 | 256 | +---------------+----+-------------+-------+""" 257 | ).strip( 258 | "\n" 259 | ) # noqa 260 | self.assertEqual(str(book), content) 261 | 262 | def testBookUsingIsave(self): 263 | fp = open(self.testfile, "rb") 264 | response = self.client.post("/polls/import_using_isave/", data={"file": fp}) 265 | eq_(response.status_code, 302) 266 | response2 = self.client.get("/polls/export/book") 267 | assert response2.status_code == 200 268 | book = pe.get_book(file_type="xls", file_content=response2.content) 269 | content = dedent( 270 | """ 271 | question: 272 | +----+---------------------------+----------------------------------------------+----------+ 273 | | id | pub_date | question_text | slug | 274 | +----+---------------------------+----------------------------------------------+----------+ 275 | | 1 | 2015-01-28T00:00:00+00:00 | What is your favourite programming language? | language | 276 | +----+---------------------------+----------------------------------------------+----------+ 277 | | 2 | 2015-01-29T00:00:00+00:00 | What is your favourite IDE? | ide | 278 | +----+---------------------------+----------------------------------------------+----------+ 279 | choice: 280 | +---------------+----+-------------+-------+ 281 | | choice_text | id | question_id | votes | 282 | +---------------+----+-------------+-------+ 283 | | Java | 1 | 1 | 0 | 284 | +---------------+----+-------------+-------+ 285 | | C++ | 2 | 1 | 0 | 286 | +---------------+----+-------------+-------+ 287 | | C | 3 | 1 | 0 | 288 | +---------------+----+-------------+-------+ 289 | | Eclipse | 4 | 2 | 0 | 290 | +---------------+----+-------------+-------+ 291 | | Visual Studio | 5 | 2 | 0 | 292 | +---------------+----+-------------+-------+ 293 | | PyCharm | 6 | 2 | 0 | 294 | +---------------+----+-------------+-------+ 295 | | IntelliJ | 7 | 2 | 0 | 296 | +---------------+----+-------------+-------+""" 297 | ).strip( 298 | "\n" 299 | ) # noqa 300 | self.assertEqual(str(book), content) 301 | 302 | def testSheet(self): 303 | fp = open(self.testfile, "rb") 304 | response = self.client.post("/polls/import/", data={"file": fp}) 305 | eq_(response.status_code, 302) 306 | response2 = self.client.get("/polls/export/sheet") 307 | assert response2.status_code == 200 308 | sheet = pe.get_sheet(file_type="xls", file_content=response2.content) 309 | content = dedent( 310 | """ 311 | question: 312 | +----+---------------------------+----------------------------------------------+----------+ 313 | | id | pub_date | question_text | slug | 314 | +----+---------------------------+----------------------------------------------+----------+ 315 | | 1 | 2015-01-28T00:00:00+00:00 | What is your favourite programming language? | language | 316 | +----+---------------------------+----------------------------------------------+----------+ 317 | | 2 | 2015-01-29T00:00:00+00:00 | What is your favourite IDE? | ide | 318 | +----+---------------------------+----------------------------------------------+----------+""" 319 | ).strip( 320 | "\n" 321 | ) # noqa 322 | assert str(sheet) == content 323 | 324 | def testImportSheet(self): 325 | fp = open("sample-sheet.xls", "rb") 326 | response = self.client.post("/polls/import_sheet/", data={"file": fp}) 327 | eq_(response.status_code, 200) 328 | response2 = self.client.get("/polls/export/sheet") 329 | eq_(response2.status_code, 200) 330 | sheet = pe.get_sheet(file_type="xls", file_content=response2.content) 331 | content = dedent( 332 | """ 333 | question: 334 | +----+---------------------------+----------------------------------------------+----------+ 335 | | id | pub_date | question_text | slug | 336 | +----+---------------------------+----------------------------------------------+----------+ 337 | | 1 | 2015-01-28T00:00:00+00:00 | What is your favourite programming language? | language | 338 | +----+---------------------------+----------------------------------------------+----------+ 339 | | 2 | 2015-01-29T00:00:00+00:00 | What is your favourite IDE? | ide | 340 | +----+---------------------------+----------------------------------------------+----------+""" 341 | ).strip( 342 | "\n" 343 | ) # noqa 344 | self.assertEqual(str(sheet), content) 345 | 346 | def testImportSheetUsingISave(self): 347 | fp = open("sample-sheet-for-isave.xls", "rb") 348 | response = self.client.post( 349 | "/polls/import_sheet_using_isave/", data={"file": fp} 350 | ) 351 | eq_(response.status_code, 200) 352 | response2 = self.client.get("/polls/export/sheet") 353 | eq_(response2.status_code, 200) 354 | sheet = pe.get_sheet(file_type="xls", file_content=response2.content) 355 | content = dedent( 356 | """ 357 | question: 358 | +----+---------------------------+----------------------------------------------+----------+ 359 | | id | pub_date | question_text | slug | 360 | +----+---------------------------+----------------------------------------------+----------+ 361 | | 1 | 2015-01-28T00:00:00+00:00 | What is your favourite programming language? | language | 362 | +----+---------------------------+----------------------------------------------+----------+ 363 | | 2 | 2015-01-29T00:00:00+00:00 | What is your favourite IDE? | ide | 364 | +----+---------------------------+----------------------------------------------+----------+""" 365 | ).strip( 366 | "\n" 367 | ) # noqa 368 | self.assertEqual(str(sheet), content) 369 | 370 | def testCustomExport(self): 371 | fp = open(self.testfile, "rb") 372 | response = self.client.post("/polls/import/", data={"file": fp}) 373 | eq_(response.status_code, 302) 374 | response2 = self.client.get("/polls/export/custom") 375 | eq_(response2.status_code, 200) 376 | sheet = pe.get_sheet(file_type="xls", file_content=response2.content) 377 | content = dedent( 378 | """ 379 | pyexcel_sheet1: 380 | +---------------+----+-------+ 381 | | choice_text | id | votes | 382 | +---------------+----+-------+ 383 | | Eclipse | 4 | 0 | 384 | +---------------+----+-------+ 385 | | Visual Studio | 5 | 0 | 386 | +---------------+----+-------+ 387 | | PyCharm | 6 | 0 | 388 | +---------------+----+-------+ 389 | | IntelliJ | 7 | 0 | 390 | +---------------+----+-------+""" 391 | ).strip("\n") 392 | self.assertEqual(str(sheet), content) 393 | 394 | 395 | @override_settings(FILE_UPLOAD_MAX_MEMORY_SIZE=1) 396 | class ExcelResponseUsingFileTestCase(ExcelResponseTestCase): 397 | pass 398 | 399 | 400 | @override_settings(FILE_UPLOAD_MAX_MEMORY_SIZE=1) 401 | class DatabaseOperationsUsingFileTestCase(DatabaseOperationsTestCase): 402 | pass 403 | 404 | 405 | class TestUploadedFile(TestCase): 406 | def test_in_memory_file(self): 407 | test_content = "a,b,c" 408 | strio = StringIO(test_content) 409 | strio.read() 410 | in_memory_file = ExcelInMemoryUploadedFile( 411 | file=strio, 412 | field_name="test", 413 | name="test_file", 414 | content_type="text", 415 | size=3, 416 | charset=None, 417 | ) 418 | params = in_memory_file.get_params() 419 | eq_(params["file_content"], test_content) 420 | -------------------------------------------------------------------------------- /tests/requirements.txt: -------------------------------------------------------------------------------- 1 | nose 2 | mock;python_version<"3" 3 | codecov 4 | coverage 5 | flake8 6 | black 7 | isort 8 | collective.checkdocs 9 | pygments 10 | moban 11 | moban_jinja2_github 12 | lxml 13 | pyexcel-ods3>=0.1.0 14 | pyexcel-xls>=0.1.0 15 | pyexcel-xlsx>=0.1.0 16 | pyexcel-handsontable 17 | pyexcel-pygal 18 | --------------------------------------------------------------------------------