├── .github └── FUNDING.yml ├── .gitignore ├── .travis.yml ├── AUTHORS.rst ├── CONTRIBUTORS.rst ├── LICENSE ├── MANIFEST.in ├── README.rst ├── REQUIREMENTS.txt ├── django_ajax ├── __init__.py ├── decorators.py ├── encoder.py ├── middleware.py ├── mixin.py ├── models.py ├── response.py └── shortcuts.py ├── setup.py └── tests ├── __init__.py ├── ajaxdecorator ├── __init__.py ├── app │ ├── __init__.py │ ├── templates │ │ └── hello.html │ ├── tests.py │ └── views.py ├── manage.py ├── settings.py └── urls.py └── ajaxmiddleware ├── __init__.py ├── app ├── __init__.py ├── templates │ └── hello.html ├── tests.py └── views.py ├── manage.py ├── settings.py └── urls.py /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | github: [yceruto] 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.log 2 | *.pot 3 | *.pyc 4 | .idea/* 5 | local_settings.py 6 | 7 | # folders generated when the packaged is installed 8 | build/* 9 | dist/* 10 | djangoajax.egg-info/* 11 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: python 2 | 3 | python: 4 | - 3.6 5 | - 3.8 6 | - 3.9 7 | 8 | env: 9 | - DJANGO_VERSION=2.* 10 | - DJANGO_VERSION=3.* 11 | - DJANGO_VERSION=4.* 12 | 13 | matrix: 14 | exclude: 15 | - python: 3.6 16 | env: DJANGO_VERSION=4.* 17 | - python: 3.8 18 | env: DJANGO_VERSION=2.* 19 | - python: 3.9 20 | env: DJANGO_VERSION=2.* 21 | 22 | install: 23 | - pip install django==$DJANGO_VERSION 24 | - python setup.py install 25 | 26 | script: 27 | - python tests/ajaxmiddleware/manage.py test app 28 | - python tests/ajaxdecorator/manage.py test app 29 | -------------------------------------------------------------------------------- /AUTHORS.rst: -------------------------------------------------------------------------------- 1 | Authors 2 | 3 | - `Yonel Ceruto Glez `_ 4 | -------------------------------------------------------------------------------- /CONTRIBUTORS.rst: -------------------------------------------------------------------------------- 1 | Contributors 2 | 3 | - `ananduee `_ 4 | - `luisza `_ 5 | - `Yasmin Vallejo Calderin `_ 6 | - `Alex Kavanaugh `_ 7 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014-2022 Yonel Ceruto Gonzalez 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | this software and associated documentation files (the "Software"), to deal in 7 | the Software without restriction, including without limitation the rights to 8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software is furnished to do so, 10 | subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 17 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 18 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 19 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 20 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /MANIFEST.in: -------------------------------------------------------------------------------- 1 | include README.rst 2 | recursive-include django_ajax * 3 | -------------------------------------------------------------------------------- /README.rst: -------------------------------------------------------------------------------- 1 | =========== 2 | django-ajax 3 | =========== 4 | 5 | Fast and easy AJAX libraries for django applications. 6 | 7 | .. image:: https://api.travis-ci.com/yceruto/django-ajax.svg?branch=master 8 | :alt: Master Build Status 9 | :target: https://travis-ci.com/github/yceruto/django-ajax 10 | 11 | .. image:: https://img.shields.io/pypi/v/djangoajax.svg 12 | :alt: PYPI Package 13 | :target: https://pypi.python.org/pypi/djangoajax 14 | 15 | .. image:: https://img.shields.io/pypi/status/django-ajax.svg 16 | :alt: PYPI Status 17 | :target: https://pypi.python.org/pypi/djangoajax 18 | 19 | .. image:: https://img.shields.io/pypi/l/djangoajax.svg 20 | :alt: PYPI License 21 | :target: https://pypi.python.org/pypi/djangoajax 22 | 23 | Requirements 24 | ------------ 25 | 26 | ``3.x`` 27 | 28 | * `python`_ >=3.5 29 | * `django`_ >=2.0 30 | 31 | ``2.x`` 32 | 33 | * `python`_ >=2.7 34 | * `django`_ >=1.7,<2.0 35 | 36 | .. _`python`: http://www.python.org/ 37 | .. _`django`: https://djangoproject.com 38 | .. _`jQuery`: http://jquery.com 39 | 40 | Installation 41 | ------------ 42 | 43 | Install django-ajax in your python environment 44 | 45 | 1- Download and install package: 46 | 47 | .. code:: sh 48 | 49 | $ pip install djangoajax 50 | 51 | Through Github: 52 | 53 | .. code:: sh 54 | 55 | pip install -e git://github.com/yceruto/django-ajax#egg=djangoajax 56 | 57 | or simply with: 58 | 59 | .. code:: sh 60 | 61 | $ python setup.py install 62 | 63 | 2- Add ``'django_ajax'`` into the ``INSTALLED_APPS`` list. 64 | 65 | 3- Read usage section and enjoy this feature! 66 | 67 | 68 | Usage 69 | ----- 70 | 71 | @ajax Decorator 72 | ~~~~~~~~~~~~~~~ 73 | 74 | .. code:: python 75 | 76 | from django_ajax.decorators import ajax 77 | 78 | @ajax 79 | def my_view(request): 80 | do_something() 81 | 82 | When the view does not return anything, you will receive this response (JSON format): 83 | 84 | .. code:: javascript 85 | 86 | {"status": 200, "statusText": "OK", "content ": null} 87 | 88 | 89 | **Sending content** 90 | 91 | .. code:: python 92 | 93 | @ajax 94 | def my_view(request): 95 | c = 2 + 3 96 | return {'result': c} 97 | 98 | The whole result is converted into a JSON format as part of the `content` element: 99 | 100 | .. code:: javascript 101 | 102 | {"status": 200, "statusText": "OK", "content": {"result": 5}} 103 | 104 | 105 | **Combining with others decorators** 106 | 107 | .. code:: python 108 | 109 | from django.contrib.auth.decorators import login_required 110 | from django_ajax.decorators import ajax 111 | 112 | @ajax 113 | @login_required 114 | def my_view(request): 115 | # if the request.user is anonymous then this view not proceed 116 | return {'user_id': request.user.id} 117 | 118 | The location or path of the redirection response will be given in the `content` item, 119 | also the `status` and `statusText` will reflect what is going on: 120 | 121 | .. code:: javascript 122 | 123 | {"status": 302, "statusText": "FOUND", "content": "/login"} 124 | 125 | 126 | **Template response** 127 | 128 | .. code:: python 129 | 130 | from django.shortcuts import render 131 | from django_ajax.decorators import ajax 132 | 133 | @ajax 134 | def my_view(request): 135 | return render(request, 'home.html') 136 | 137 | The JSON response: 138 | 139 | .. code:: javascript 140 | 141 | {"status": 200, "statusText": "OK", "content": "..."} 142 | 143 | 144 | **Catch exceptions** 145 | 146 | .. code:: python 147 | 148 | @ajax 149 | def my_view(request): 150 | a = 23 / 0 # this line throws an exception 151 | return a 152 | 153 | The JSON response: 154 | 155 | .. code:: javascript 156 | 157 | {"status": 500, "statusText": "INTERNAL SERVER ERROR", "content": "integer division or modulo by zero"} 158 | 159 | 160 | AJAXMiddleware 161 | ~~~~~~~~~~~~~~ 162 | 163 | If you are using AJAX at all times in your project, we suggest you activate the AJAXMiddleware described below. 164 | 165 | Add ``django_ajax.middleware.AJAXMiddleware`` to the ``MIDDLEWARE_CLASSES`` list in ``settings.py`` and all your responses will be converted to JSON whereas the request was made via AJAX, otherwise it will return a normal HttpResponse. 166 | 167 | .. caution:: If this middleware is activated you cannot use the ``@ajax`` decorator. That will cause double JSON conversion. 168 | 169 | 170 | AJAXMixin for class-based views 171 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 172 | 173 | ``AJAXMixin`` is an object that call to AJAX decorator. 174 | 175 | .. code:: python 176 | 177 | from django.views.generic import TemplateView 178 | from django_ajax.mixin import AJAXMixin 179 | 180 | class SimpleView(AJAXMixin, TemplateView): 181 | template_name = 'home.html' 182 | 183 | The JSON response: 184 | 185 | .. code:: javascript 186 | 187 | {"status": 200, "statusText": "OK", "content": "..."} 188 | 189 | Enjoy And Share! 190 | -------------------------------------------------------------------------------- /REQUIREMENTS.txt: -------------------------------------------------------------------------------- 1 | django>=2.0 2 | -------------------------------------------------------------------------------- /django_ajax/__init__.py: -------------------------------------------------------------------------------- 1 | """ 2 | Init 3 | """ 4 | from __future__ import unicode_literals 5 | 6 | import datetime 7 | import os 8 | import subprocess 9 | 10 | VERSION = (3, 3, 0, 'final', 0) 11 | 12 | 13 | def get_version(version=None): 14 | """ 15 | Returns a PEP 386-compliant version number from VERSION. 16 | """ 17 | if not version: 18 | version = VERSION 19 | else: 20 | assert len(version) == 5 21 | assert version[3] in ('alpha', 'beta', 'rc', 'final') 22 | 23 | # Now build the two parts of the version number: 24 | # main = X.Y[.Z] 25 | # sub = .devN - for pre-alpha releases 26 | # | {a|b|c}N - for alpha, beta and rc releases 27 | 28 | parts = 2 if version[2] == 0 else 3 29 | main = '.'.join(str(x) for x in version[:parts]) 30 | 31 | sub = '' 32 | if version[3] == 'alpha' and version[4] == 0: 33 | git_changeset = get_git_changeset() 34 | if git_changeset: 35 | sub = '.dev%s' % git_changeset 36 | 37 | elif version[3] != 'final': 38 | mapping = {'alpha': 'a', 'beta': 'b', 'rc': 'c'} 39 | sub = mapping[version[3]] + str(version[4]) 40 | 41 | return str(main + sub) 42 | 43 | 44 | def get_git_changeset(): 45 | """ 46 | Returns a numeric identifier of the latest git changeset. 47 | 48 | The result is the UTC timestamp of the changeset in YYYYMMDDHHMMSS format. 49 | This value isn't guaranteed to be unique, but collisions are very unlikely, 50 | so it's sufficient for generating the development version numbers. 51 | """ 52 | repo_dir = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) 53 | git_log = subprocess.Popen('git log --pretty=format:%ct --quiet -1 HEAD', 54 | stdout=subprocess.PIPE, stderr=subprocess.PIPE, 55 | shell=True, cwd=repo_dir, 56 | universal_newlines=True) 57 | timestamp = git_log.communicate()[0] 58 | 59 | try: 60 | timestamp = datetime.datetime.utcfromtimestamp(int(timestamp)) 61 | except ValueError: 62 | return None 63 | 64 | return timestamp.strftime('%Y%m%d%H%M%S') 65 | -------------------------------------------------------------------------------- /django_ajax/decorators.py: -------------------------------------------------------------------------------- 1 | """ 2 | Decorators 3 | """ 4 | from __future__ import unicode_literals 5 | 6 | from functools import wraps, WRAPPER_ASSIGNMENTS 7 | 8 | from django.http import HttpResponseBadRequest 9 | 10 | from django_ajax.shortcuts import render_to_json 11 | 12 | 13 | def ajax(function=None, mandatory=True, **ajax_kwargs): 14 | """ 15 | Decorator who guesses the user response type and translates to a serialized 16 | JSON response. Usage:: 17 | 18 | @ajax 19 | def my_view(request): 20 | do_something() 21 | # will send {'status': 200, 'statusText': 'OK', 'content': null} 22 | 23 | @ajax 24 | def my_view(request): 25 | return {'key': 'value'} 26 | # will send {'status': 200, 'statusText': 'OK', 27 | 'content': {'key': 'value'}} 28 | 29 | @ajax 30 | def my_view(request): 31 | return HttpResponse('

Hi!

') 32 | # will send {'status': 200, 'statusText': 'OK', 33 | 'content': '

Hi!

'} 34 | 35 | @ajax 36 | def my_view(request): 37 | return redirect('home') 38 | # will send {'status': 302, 'statusText': 'FOUND', 'content': '/'} 39 | 40 | # combination with others decorators: 41 | 42 | @ajax 43 | @login_required 44 | @require_POST 45 | def my_view(request): 46 | pass 47 | # if request user is not authenticated then the @login_required 48 | # decorator redirect to login page. 49 | # will send {'status': 302, 'statusText': 'FOUND', 50 | 'content': '/login'} 51 | 52 | # if request method is 'GET' then the @require_POST decorator return 53 | # a HttpResponseNotAllowed response. 54 | # will send {'status': 405, 'statusText': 'METHOD NOT ALLOWED', 55 | 'content': null} 56 | 57 | """ 58 | def decorator(func): 59 | @wraps(func, assigned=WRAPPER_ASSIGNMENTS) 60 | def inner(request, *args, **kwargs): 61 | if mandatory and not request.headers.get('x-requested-with') == 'XMLHttpRequest': 62 | return HttpResponseBadRequest() 63 | 64 | if request.headers.get('x-requested-with') == 'XMLHttpRequest': 65 | # return json response 66 | try: 67 | return render_to_json(func(request, *args, **kwargs), request, **ajax_kwargs) 68 | except Exception as exception: 69 | return render_to_json(exception, request) 70 | else: 71 | # return standard response 72 | return func(request, *args, **kwargs) 73 | 74 | return inner 75 | 76 | if function: 77 | return decorator(function) 78 | 79 | return decorator 80 | -------------------------------------------------------------------------------- /django_ajax/encoder.py: -------------------------------------------------------------------------------- 1 | """ 2 | Utils 3 | """ 4 | from __future__ import unicode_literals 5 | 6 | import json 7 | from datetime import date 8 | from django.http.response import HttpResponseRedirectBase, HttpResponse 9 | from django.template.response import TemplateResponse 10 | from django.utils.encoding import force_str 11 | from django.db.models.base import ModelBase 12 | from decimal import Decimal 13 | 14 | 15 | class LazyJSONEncoderMixin(object): 16 | """ 17 | A JSONEncoder subclass that handle querysets and models objects. 18 | Add how handle your type of object here to use when dump json 19 | 20 | """ 21 | 22 | def default(self, obj): 23 | # handles HttpResponse and exception content 24 | if issubclass(type(obj), HttpResponseRedirectBase): 25 | return obj['Location'] 26 | elif issubclass(type(obj), TemplateResponse): 27 | return obj.rendered_content 28 | elif issubclass(type(obj), HttpResponse): 29 | return obj.content 30 | elif issubclass(type(obj), Exception) or isinstance(obj, bytes): 31 | return force_str(obj) 32 | 33 | # this handles querysets and other iterable types 34 | try: 35 | iterable = iter(obj) 36 | except TypeError: 37 | pass 38 | else: 39 | return list(iterable) 40 | 41 | # this handlers Models 42 | if isinstance(obj.__class__, ModelBase): 43 | return force_str(obj) 44 | 45 | if isinstance(obj, Decimal): 46 | return float(obj) 47 | 48 | if isinstance(obj, date): 49 | return obj.isoformat() 50 | 51 | return super(LazyJSONEncoderMixin, self).default(obj) 52 | 53 | 54 | class LazyJSONEncoder(LazyJSONEncoderMixin, json.JSONEncoder): 55 | pass 56 | 57 | 58 | def serialize_to_json(data, **kwargs): 59 | """ 60 | A wrapper for simplejson.dumps with defaults as: 61 | 62 | cls=LazyJSONEncoder 63 | 64 | All arguments can be added via kwargs 65 | """ 66 | kwargs['cls'] = kwargs.get('cls', LazyJSONEncoder) 67 | 68 | return json.dumps(data, **kwargs) 69 | -------------------------------------------------------------------------------- /django_ajax/middleware.py: -------------------------------------------------------------------------------- 1 | """ 2 | Middleware 3 | """ 4 | from __future__ import unicode_literals 5 | 6 | from django_ajax.shortcuts import render_to_json 7 | from django.utils.deprecation import MiddlewareMixin 8 | 9 | 10 | class AJAXMiddleware(MiddlewareMixin): 11 | """ 12 | AJAX Middleware that decides when to convert the response to JSON. 13 | """ 14 | 15 | def process_response(self, request, response): 16 | """ 17 | If the request was made by AJAX then convert response to JSON, 18 | otherwise return the original response. 19 | """ 20 | if request.headers.get('x-requested-with') == 'XMLHttpRequest': 21 | return render_to_json(response) 22 | return response 23 | 24 | def process_exception(self, request, exception): 25 | """ 26 | Catch exception if the request was made by AJAX, 27 | after will become up on JSON. 28 | """ 29 | if request.headers.get('x-requested-with') == 'XMLHttpRequest': 30 | return exception 31 | -------------------------------------------------------------------------------- /django_ajax/mixin.py: -------------------------------------------------------------------------------- 1 | """ 2 | Mixin Response 3 | """ 4 | from __future__ import unicode_literals 5 | 6 | from django_ajax.decorators import ajax 7 | 8 | 9 | class AJAXMixin(object): 10 | 11 | """ 12 | AJAX Mixin Class 13 | """ 14 | ajax_mandatory = True 15 | json_encoder = None 16 | 17 | def dispatch(self, request, *args, **kwargs): 18 | """ 19 | Using ajax decorator 20 | """ 21 | ajax_kwargs = {'mandatory': self.ajax_mandatory} 22 | if self.json_encoder: 23 | ajax_kwargs['cls'] = self.json_encoder 24 | 25 | return ajax(**ajax_kwargs)(super(AJAXMixin, self).dispatch)(request, *args, **kwargs) 26 | -------------------------------------------------------------------------------- /django_ajax/models.py: -------------------------------------------------------------------------------- 1 | # models 2 | -------------------------------------------------------------------------------- /django_ajax/response.py: -------------------------------------------------------------------------------- 1 | """ 2 | Responses 3 | """ 4 | from __future__ import unicode_literals 5 | 6 | from django.conf import settings 7 | from django.http import HttpResponse 8 | 9 | from django_ajax.encoder import serialize_to_json 10 | 11 | 12 | class JSONResponse(HttpResponse): 13 | """ 14 | Return a JSON serialized HTTP response 15 | """ 16 | 17 | def __init__(self, data, **kwargs): 18 | """ 19 | This returns a object that we send as json content using 20 | utils.serialize_to_json, that is a wrapper to json.dumps 21 | method using a custom class to handle models and querysets. Put your 22 | options to serialize_to_json in kwargs, other options are used by 23 | response. 24 | """ 25 | if 'sort_keys' not in kwargs: 26 | kwargs['sort_keys'] = settings.DEBUG 27 | 28 | super(JSONResponse, self).__init__( 29 | content=serialize_to_json(data, **kwargs), 30 | content_type='application/json' 31 | ) 32 | -------------------------------------------------------------------------------- /django_ajax/shortcuts.py: -------------------------------------------------------------------------------- 1 | """ 2 | Shortcuts 3 | """ 4 | from __future__ import unicode_literals 5 | 6 | import logging 7 | 8 | from django.conf import settings 9 | from django.http.response import Http404, HttpResponseServerError 10 | from django.views.debug import ExceptionReporter 11 | 12 | from django_ajax.response import JSONResponse 13 | 14 | logger = logging.getLogger(__name__) 15 | 16 | # Available since django 1.6 17 | REASON_PHRASES = { 18 | 100: 'CONTINUE', 19 | 101: 'SWITCHING PROTOCOLS', 20 | 102: 'PROCESSING', 21 | 200: 'OK', 22 | 201: 'CREATED', 23 | 202: 'ACCEPTED', 24 | 203: 'NON-AUTHORITATIVE INFORMATION', 25 | 204: 'NO CONTENT', 26 | 205: 'RESET CONTENT', 27 | 206: 'PARTIAL CONTENT', 28 | 207: 'MULTI-STATUS', 29 | 208: 'ALREADY REPORTED', 30 | 226: 'IM USED', 31 | 300: 'MULTIPLE CHOICES', 32 | 301: 'MOVED PERMANENTLY', 33 | 302: 'FOUND', 34 | 303: 'SEE OTHER', 35 | 304: 'NOT MODIFIED', 36 | 305: 'USE PROXY', 37 | 306: 'RESERVED', 38 | 307: 'TEMPORARY REDIRECT', 39 | 400: 'BAD REQUEST', 40 | 401: 'UNAUTHORIZED', 41 | 402: 'PAYMENT REQUIRED', 42 | 403: 'FORBIDDEN', 43 | 404: 'NOT FOUND', 44 | 405: 'METHOD NOT ALLOWED', 45 | 406: 'NOT ACCEPTABLE', 46 | 407: 'PROXY AUTHENTICATION REQUIRED', 47 | 408: 'REQUEST TIMEOUT', 48 | 409: 'CONFLICT', 49 | 410: 'GONE', 50 | 411: 'LENGTH REQUIRED', 51 | 412: 'PRECONDITION FAILED', 52 | 413: 'REQUEST ENTITY TOO LARGE', 53 | 414: 'REQUEST-URI TOO LONG', 54 | 415: 'UNSUPPORTED MEDIA TYPE', 55 | 416: 'REQUESTED RANGE NOT SATISFIABLE', 56 | 417: 'EXPECTATION FAILED', 57 | 418: "I'M A TEAPOT", 58 | 422: 'UNPROCESSABLE ENTITY', 59 | 423: 'LOCKED', 60 | 424: 'FAILED DEPENDENCY', 61 | 426: 'UPGRADE REQUIRED', 62 | 428: 'PRECONDITION REQUIRED', 63 | 429: 'TOO MANY REQUESTS', 64 | 431: 'REQUEST HEADER FIELDS TOO LARGE', 65 | 500: 'INTERNAL SERVER ERROR', 66 | 501: 'NOT IMPLEMENTED', 67 | 502: 'BAD GATEWAY', 68 | 503: 'SERVICE UNAVAILABLE', 69 | 504: 'GATEWAY TIMEOUT', 70 | 505: 'HTTP VERSION NOT SUPPORTED', 71 | 506: 'VARIANT ALSO NEGOTIATES', 72 | 507: 'INSUFFICIENT STORAGE', 73 | 508: 'LOOP DETECTED', 74 | 510: 'NOT EXTENDED', 75 | 511: 'NETWORK AUTHENTICATION REQUIRED', 76 | } 77 | 78 | 79 | def render_to_json(response, request=None, **kwargs): 80 | """ 81 | Creates the main structure and returns the JSON response. 82 | """ 83 | # determine the status code 84 | if hasattr(response, 'status_code'): 85 | status_code = response.status_code 86 | elif issubclass(type(response), Http404): 87 | status_code = 404 88 | elif issubclass(type(response), Exception): 89 | status_code = 500 90 | logger.exception(str(response), extra={'request': request}) 91 | 92 | if settings.DEBUG: 93 | import sys 94 | reporter = ExceptionReporter(None, *sys.exc_info()) 95 | text = reporter.get_traceback_text() 96 | response = HttpResponseServerError(text, content_type='text/plain') 97 | else: 98 | response = HttpResponseServerError("An error occured while processing an AJAX request.", content_type='text/plain') 99 | else: 100 | status_code = 200 101 | 102 | # creating main structure 103 | data = { 104 | 'status': status_code, 105 | 'statusText': REASON_PHRASES.get(status_code, 'UNKNOWN STATUS CODE'), 106 | 'content': response 107 | } 108 | 109 | return JSONResponse(data, **kwargs) 110 | 111 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | from setuptools import setup, find_packages 2 | import os 3 | 4 | README = open(os.path.join(os.path.dirname(__file__), 'README.rst')).read() 5 | 6 | # allow setup.py to be run from any path 7 | os.chdir(os.path.normpath(os.path.join(os.path.abspath(__file__), os.pardir))) 8 | 9 | # Dynamically calculate the version based on django.VERSION. 10 | version = __import__('django_ajax').get_version() 11 | 12 | setup( 13 | name='djangoajax', 14 | 15 | # Versions should comply with PEP440. For a discussion on single-sourcing 16 | # the version across setup.py and the project code, see 17 | # https://packaging.python.org/en/latest/single_source_version.html 18 | version=version, 19 | 20 | description='Powerful and easy AJAX framework for django applications.', 21 | long_description=README, 22 | 23 | # The project's main homepage. 24 | url='https://github.com/yceruto/django-ajax', 25 | 26 | # Author details 27 | author='Yonel Ceruto Glez', 28 | author_email='yonelceruto@gmail.com', 29 | 30 | license='MIT', 31 | 32 | classifiers=[ 33 | 'Environment :: Web Environment', 34 | 'Framework :: Django', 35 | 'Intended Audience :: Developers', 36 | 'License :: OSI Approved :: MIT License', 37 | 'Operating System :: OS Independent', 38 | 'Programming Language :: Python', 39 | 'Programming Language :: Python :: 3.5', 40 | 'Topic :: Internet :: WWW/HTTP', 41 | 'Topic :: Internet :: WWW/HTTP :: Dynamic Content', 42 | ], 43 | 44 | keywords='ajax django-ajax json', 45 | 46 | packages=find_packages(), 47 | 48 | platforms=['OS Independent'], 49 | 50 | install_requires=[ 51 | 'django>=2.0', 52 | ], 53 | 54 | include_package_data=True, 55 | zip_safe=False 56 | ) 57 | -------------------------------------------------------------------------------- /tests/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yceruto/django-ajax/924ee6c8763226a8f18f661a717c6ebb63451aab/tests/__init__.py -------------------------------------------------------------------------------- /tests/ajaxdecorator/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yceruto/django-ajax/924ee6c8763226a8f18f661a717c6ebb63451aab/tests/ajaxdecorator/__init__.py -------------------------------------------------------------------------------- /tests/ajaxdecorator/app/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yceruto/django-ajax/924ee6c8763226a8f18f661a717c6ebb63451aab/tests/ajaxdecorator/app/__init__.py -------------------------------------------------------------------------------- /tests/ajaxdecorator/app/templates/hello.html: -------------------------------------------------------------------------------- 1 | Hello 2 | -------------------------------------------------------------------------------- /tests/ajaxdecorator/app/tests.py: -------------------------------------------------------------------------------- 1 | from __future__ import unicode_literals 2 | from datetime import datetime 3 | from django.test import TestCase 4 | from django_ajax.encoder import LazyJSONEncoder 5 | import json 6 | 7 | 8 | class LazyJSONEncoderMixinTestCase(TestCase): 9 | def test_default_date(self): 10 | data = {'datetime': datetime.today()} 11 | self.assertEqual('{"datetime": "' + data['datetime'].isoformat() + '"}', json.dumps(data, cls=LazyJSONEncoder)) 12 | 13 | 14 | class BaseTestCase(TestCase): 15 | def post(self, uri, data=None): 16 | response = resp = self.client.get(uri, data, HTTP_X_REQUESTED_WITH='XMLHttpRequest') 17 | 18 | self.assertEquals(200, resp.status_code) 19 | self.assertEquals('application/json', response['Content-Type']) 20 | if isinstance(response.content, str): 21 | return response, json.loads(response.content) 22 | else: 23 | return response, json.loads(response.content.decode('utf-8')) 24 | 25 | 26 | class FooTestCase(BaseTestCase): 27 | def test_json_response(self): 28 | resp, data = self.post('/ajax/foo') 29 | 30 | self.assertEqual('OK', data['statusText']) 31 | self.assertEqual({'foo': True}, data['content']) 32 | 33 | 34 | class LoginRequiredTestCase(BaseTestCase): 35 | def test_json_response(self): 36 | resp, data = self.post('/ajax/login-required') 37 | 38 | self.assertEquals(302, data['status']) 39 | self.assertEqual('FOUND', data['statusText']) 40 | 41 | 42 | class RenderTestCase(BaseTestCase): 43 | def test_json_response(self): 44 | resp, data = self.post('/ajax/render') 45 | 46 | self.assertEquals(200, data['status']) 47 | self.assertEqual('OK', data['statusText']) 48 | self.assertEqual('Hello', data['content'].strip()) 49 | 50 | 51 | class RenderClassBasedViewTestCase(BaseTestCase): 52 | def test_json_response(self): 53 | resp, data = self.post('/ajax/render-class-based-view') 54 | 55 | self.assertEquals(200, data['status']) 56 | self.assertEqual('OK', data['statusText']) 57 | self.assertEqual('Hello', data['content'].strip()) 58 | 59 | 60 | class ExceptionTestCase(BaseTestCase): 61 | def test_json_response(self): 62 | resp, data = self.post('/ajax/exception') 63 | 64 | # self.assertEquals(200, data['status']) 65 | self.assertEqual('INTERNAL SERVER ERROR', data['statusText']) 66 | 67 | 68 | class RaiseExceptionTestCase(BaseTestCase): 69 | def test_json_response(self): 70 | resp, data = self.post('/ajax/raise-exception') 71 | 72 | # self.assertEquals(200, data['status']) 73 | self.assertEqual('NOT FOUND', data['statusText']) 74 | -------------------------------------------------------------------------------- /tests/ajaxdecorator/app/views.py: -------------------------------------------------------------------------------- 1 | from django.contrib.auth.decorators import login_required 2 | from django.views.generic import TemplateView 3 | from django.shortcuts import render 4 | from django.http import Http404 5 | 6 | from django_ajax.decorators import ajax 7 | from django_ajax.mixin import AJAXMixin 8 | 9 | 10 | @ajax 11 | def foo_view(request): 12 | return {'foo': True} 13 | 14 | 15 | @ajax 16 | @login_required 17 | def login_required_view(request): 18 | # if the request.user is anonymous then this view not proceed 19 | return {'user_id': request.user.id} 20 | 21 | 22 | @ajax 23 | def render_view(request): 24 | return render(request, 'hello.html') 25 | 26 | 27 | class SimpleView(AJAXMixin, TemplateView): 28 | template_name = 'hello.html' 29 | 30 | 31 | @ajax 32 | def exception_view(request): 33 | a = 23 / 0 # this line throws an exception 34 | return a 35 | 36 | 37 | @ajax 38 | def raise_exception_view(request): 39 | raise Http404 40 | -------------------------------------------------------------------------------- /tests/ajaxdecorator/manage.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | from __future__ import absolute_import 3 | 4 | import os 5 | import sys 6 | 7 | if __name__ == "__main__": 8 | os.environ.setdefault("DJANGO_SETTINGS_MODULE", "settings") 9 | 10 | from django.core.management import execute_from_command_line 11 | 12 | execute_from_command_line(sys.argv) 13 | -------------------------------------------------------------------------------- /tests/ajaxdecorator/settings.py: -------------------------------------------------------------------------------- 1 | from __future__ import absolute_import 2 | 3 | import os 4 | import sys 5 | 6 | sys.path.insert(0, '../') 7 | 8 | ROOT_DIR = os.path.dirname(__file__) 9 | 10 | DEBUG = True 11 | TEMPLATE_DEBUG = DEBUG 12 | 13 | ADMINS = ( 14 | ('Yonel Ceruto', 'yonelceruto@gmail.com'), 15 | ) 16 | 17 | MANAGERS = ADMINS 18 | 19 | DATABASES = { 20 | 'default': { 21 | 'ENGINE': 'django.db.backends.sqlite3', 22 | 'NAME': os.path.join(ROOT_DIR, 'data.sqlite3'), 23 | } 24 | } 25 | 26 | # A boolean that specifies if datetimes will be timezone-aware by default or 27 | # not. If this is set to True, Django will use timezone-aware datetimes 28 | # internally. Otherwise, Django will use naive datetimes in local time. 29 | USE_TZ = True 30 | 31 | # Local time zone for this installation. Choices can be found here: 32 | # http://en.wikipedia.org/wiki/List_of_tz_zones_by_name 33 | # although not all choices may be available on all operating systems. 34 | # On Unix systems, a value of None will cause Django to use the same 35 | # timezone as the operating system. 36 | # If running in a Windows environment this must be set to the same as your 37 | # system time zone. 38 | TIME_ZONE = 'UTC' 39 | 40 | # Language code for this installation. All choices can be found here: 41 | # http://www.i18nguy.com/unicode/language-identifiers.html 42 | LANGUAGE_CODE = 'en-us' 43 | 44 | SITE_ID = 1 45 | 46 | # If you set this to False, Django will make some optimizations so as not 47 | # to load the internationalization machinery. 48 | USE_I18N = True 49 | 50 | # If you set this to False, Django will not format dates, numbers and 51 | # calendars according to the current locale 52 | USE_L10N = True 53 | 54 | # Absolute filesystem path to the directory that will hold user-uploaded files. 55 | # Example: "/home/media/media.lawrence.com/media/" 56 | MEDIA_ROOT = '' 57 | 58 | # URL that handles the media served from MEDIA_ROOT. Make sure to use a 59 | # trailing slash. 60 | # Examples: "http://media.lawrence.com/media/", "http://example.com/media/" 61 | MEDIA_URL = '' 62 | 63 | # Absolute path to the directory static files should be collected to. 64 | # Don't put anything in this directory yourself; store your static files 65 | # in apps' "static/" subdirectories and in STATICFILES_DIRS. 66 | # Example: "/home/media/media.lawrence.com/static/" 67 | STATIC_ROOT = '' 68 | 69 | # URL prefix for static files. 70 | # Example: "http://media.lawrence.com/static/" 71 | STATIC_URL = '/static/' 72 | 73 | # URL prefix for admin static files -- CSS, JavaScript and images. 74 | # Make sure to use a trailing slash. 75 | # Examples: "http://foo.com/static/admin/", "/static/admin/". 76 | ADMIN_MEDIA_PREFIX = '/static/admin/' 77 | 78 | # Make this unique, and don't share it with anybody. 79 | SECRET_KEY = '(l*w&8lg1co#vw#3$1#i^!!!tvhiw061%@jm*_-#_o@jv-y^#d' 80 | 81 | MIDDLEWARE = ( 82 | 'django.middleware.security.SecurityMiddleware', 83 | 'django.contrib.sessions.middleware.SessionMiddleware', 84 | 'django.middleware.common.CommonMiddleware', 85 | 'django.middleware.csrf.CsrfViewMiddleware', 86 | 'django.contrib.auth.middleware.AuthenticationMiddleware', 87 | 'django.contrib.messages.middleware.MessageMiddleware', 88 | 'django.middleware.clickjacking.XFrameOptionsMiddleware', 89 | ) 90 | 91 | ROOT_URLCONF = 'urls' 92 | 93 | TEMPLATES = [ 94 | { 95 | 'BACKEND': 'django.template.backends.django.DjangoTemplates', 96 | 'DIRS': [os.path.join(ROOT_DIR, 'app/templates')], 97 | 'APP_DIRS': True, 98 | 'OPTIONS': { 99 | 'context_processors': [ 100 | 'django.template.context_processors.debug', 101 | 'django.template.context_processors.request', 102 | 'django.contrib.auth.context_processors.auth', 103 | 'django.contrib.messages.context_processors.messages', 104 | ], 105 | }, 106 | }, 107 | ] 108 | 109 | INSTALLED_APPS = ( 110 | 'app', 111 | 'django.contrib.admin', 112 | 'django.contrib.auth', 113 | 'django.contrib.contenttypes', 114 | 'django.contrib.sessions', 115 | 'django.contrib.messages', 116 | 'django.contrib.staticfiles', 117 | ) 118 | 119 | AUTH_PASSWORD_VALIDATORS = [ 120 | { 121 | 'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator', 122 | }, 123 | { 124 | 'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator', 125 | }, 126 | { 127 | 'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator', 128 | }, 129 | { 130 | 'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator', 131 | }, 132 | ] 133 | 134 | # A sample logging configuration. The only tangible logging 135 | # performed by this configuration is to send an email to 136 | # the site admins on every HTTP 500 error. 137 | # See http://docs.djangoproject.com/en/dev/topics/logging for 138 | # more details on how to customize your logging configuration. 139 | LOGGING = { 140 | 'version': 1, 141 | 'disable_existing_loggers': False, 142 | 'handlers': { 143 | 'mail_admins': { 144 | 'level': 'ERROR', 145 | 'filters': [], 146 | 'class': 'django.utils.log.AdminEmailHandler' 147 | } 148 | }, 149 | 'loggers': { 150 | 'django.request': { 151 | 'handlers': ['mail_admins'], 152 | 'level': 'ERROR', 153 | 'propagate': True, 154 | }, 155 | } 156 | } 157 | 158 | # Only run Jenkins report generation on these apps. 159 | PROJECT_APPS = ('app',) 160 | 161 | # Which Jenkins reports/tasks to run. 162 | JENKINS_TASKS = ('django_jenkins.tasks.run_pylint', 163 | 'django_jenkins.tasks.run_pep8', 164 | 'django_jenkins.tasks.run_pyflakes', 165 | 'django_jenkins.tasks.with_coverage', 166 | 'django_jenkins.tasks.django_tests',) 167 | 168 | # The test runner for the Jenkins command. 169 | JENKINS_TEST_RUNNER = 'django_jenkins.runner.CITestSuiteRunner' 170 | 171 | # django-ajax specific settings 172 | MAX_PER_PAGE = 20 173 | -------------------------------------------------------------------------------- /tests/ajaxdecorator/urls.py: -------------------------------------------------------------------------------- 1 | from __future__ import absolute_import 2 | from app import views 3 | import django 4 | 5 | try: 6 | try: 7 | from django.conf.urls import patterns, include, url 8 | except ImportError: 9 | from django.conf.urls import include, url 10 | except ImportError: 11 | from django.urls import include, re_path 12 | 13 | if django.VERSION < (1, 8): 14 | urlpatterns = patterns('', 15 | url(r'^ajax/foo$', views.foo_view, name='foo'), 16 | url(r'^ajax/login-required$', views.login_required_view, name='login_required'), 17 | url(r'^ajax/render$', views.render_view, name='render'), 18 | url(r'^ajax/render-class-based-view$', views.SimpleView.as_view(), name='render_class_based_view'), 19 | url(r'^ajax/exception$', views.exception_view, name='exception'), 20 | url(r'^ajax/raise-exception$', views.raise_exception_view, name='raise_exception'), 21 | ) 22 | elif django.VERSION < (4, 0): 23 | urlpatterns = [ 24 | url(r'^ajax/foo$', views.foo_view, name='foo'), 25 | url(r'^ajax/login-required$', views.login_required_view, name='login_required'), 26 | url(r'^ajax/render$', views.render_view, name='render'), 27 | url(r'^ajax/render-class-based-view$', views.SimpleView.as_view(), name='render_class_based_view'), 28 | url(r'^ajax/exception$', views.exception_view, name='exception'), 29 | url(r'^ajax/raise-exception$', views.raise_exception_view, name='raise_exception'), 30 | ] 31 | else: 32 | urlpatterns = [ 33 | re_path(r'^ajax/foo$', views.foo_view, name='foo'), 34 | re_path(r'^ajax/login-required$', views.login_required_view, name='login_required'), 35 | re_path(r'^ajax/render$', views.render_view, name='render'), 36 | re_path(r'^ajax/render-class-based-view$', views.SimpleView.as_view(), name='render_class_based_view'), 37 | re_path(r'^ajax/exception$', views.exception_view, name='exception'), 38 | re_path(r'^ajax/raise-exception$', views.raise_exception_view, name='raise_exception'), 39 | ] 40 | -------------------------------------------------------------------------------- /tests/ajaxmiddleware/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yceruto/django-ajax/924ee6c8763226a8f18f661a717c6ebb63451aab/tests/ajaxmiddleware/__init__.py -------------------------------------------------------------------------------- /tests/ajaxmiddleware/app/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yceruto/django-ajax/924ee6c8763226a8f18f661a717c6ebb63451aab/tests/ajaxmiddleware/app/__init__.py -------------------------------------------------------------------------------- /tests/ajaxmiddleware/app/templates/hello.html: -------------------------------------------------------------------------------- 1 | Hello 2 | -------------------------------------------------------------------------------- /tests/ajaxmiddleware/app/tests.py: -------------------------------------------------------------------------------- 1 | from __future__ import unicode_literals 2 | from datetime import datetime 3 | from django.test import TestCase 4 | from django_ajax.encoder import LazyJSONEncoder 5 | import json 6 | 7 | 8 | class LazyJSONEncoderMixinTestCase(TestCase): 9 | def test_default_date(self): 10 | data = {'datetime': datetime.today()} 11 | self.assertEqual('{"datetime": "' + data['datetime'].isoformat() + '"}', json.dumps(data, cls=LazyJSONEncoder)) 12 | 13 | 14 | class BaseTestCase(TestCase): 15 | def post(self, uri, data=None): 16 | response = resp = self.client.get(uri, data, HTTP_X_REQUESTED_WITH='XMLHttpRequest') 17 | 18 | self.assertEquals(200, resp.status_code) 19 | self.assertEquals('application/json', response['Content-Type']) 20 | if isinstance(response.content, str): 21 | return response, json.loads(response.content) 22 | else: 23 | return response, json.loads(response.content.decode('utf-8')) 24 | 25 | 26 | class FooTestCase(BaseTestCase): 27 | def test_json_response(self): 28 | resp, data = self.post('/ajax/foo') 29 | 30 | self.assertEqual('OK', data['statusText']) 31 | self.assertEqual({'foo': True}, data['content']) 32 | 33 | 34 | class LoginRequiredTestCase(BaseTestCase): 35 | def test_json_response(self): 36 | resp, data = self.post('/ajax/login-required') 37 | 38 | self.assertEquals(302, data['status']) 39 | self.assertEqual('FOUND', data['statusText']) 40 | 41 | 42 | class RenderTestCase(BaseTestCase): 43 | def test_json_response(self): 44 | resp, data = self.post('/ajax/render') 45 | 46 | self.assertEquals(200, data['status']) 47 | self.assertEqual('OK', data['statusText']) 48 | self.assertEqual('Hello', data['content'].strip()) 49 | 50 | 51 | class RenderClassBasedViewTestCase(BaseTestCase): 52 | def test_json_response(self): 53 | resp, data = self.post('/ajax/render-class-based-view') 54 | 55 | self.assertEquals(200, data['status']) 56 | self.assertEqual('OK', data['statusText']) 57 | self.assertEqual('Hello', data['content'].strip()) 58 | 59 | 60 | class ExceptionTestCase(BaseTestCase): 61 | def test_json_response(self): 62 | resp, data = self.post('/ajax/exception') 63 | 64 | # self.assertEquals(200, data['status']) 65 | self.assertEqual('INTERNAL SERVER ERROR', data['statusText']) 66 | 67 | 68 | class RaiseExceptionTestCase(BaseTestCase): 69 | def test_json_response(self): 70 | resp, data = self.post('/ajax/raise-exception') 71 | 72 | # self.assertEquals(200, data['status']) 73 | self.assertEqual('NOT FOUND', data['statusText']) 74 | -------------------------------------------------------------------------------- /tests/ajaxmiddleware/app/views.py: -------------------------------------------------------------------------------- 1 | from django.contrib.auth.decorators import login_required 2 | from django.views.generic import TemplateView 3 | from django.shortcuts import render 4 | from django.http import Http404 5 | 6 | # We're using `django_ajax.middleware.AJAXMiddleware` in settings 7 | # so we don't need to use `@ajax` and `AJAXMixin` decorators 8 | 9 | 10 | def foo_view(request): 11 | return {'foo': True} 12 | 13 | 14 | @login_required 15 | def login_required_view(request): 16 | # if the request.user is anonymous then this view not proceed 17 | return {'user_id': request.user.id} 18 | 19 | 20 | def render_view(request): 21 | return render(request, 'hello.html') 22 | 23 | 24 | class SimpleView(TemplateView): 25 | template_name = 'hello.html' 26 | 27 | 28 | def exception_view(request): 29 | a = 23 / 0 # this line throws an exception 30 | return a 31 | 32 | 33 | def raise_exception_view(request): 34 | raise Http404 35 | -------------------------------------------------------------------------------- /tests/ajaxmiddleware/manage.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | from __future__ import absolute_import 3 | 4 | import os 5 | import sys 6 | 7 | if __name__ == "__main__": 8 | os.environ.setdefault("DJANGO_SETTINGS_MODULE", "settings") 9 | 10 | from django.core.management import execute_from_command_line 11 | 12 | execute_from_command_line(sys.argv) 13 | -------------------------------------------------------------------------------- /tests/ajaxmiddleware/settings.py: -------------------------------------------------------------------------------- 1 | from __future__ import absolute_import 2 | 3 | import os 4 | import sys 5 | 6 | sys.path.insert(0, '../') 7 | 8 | ROOT_DIR = os.path.dirname(__file__) 9 | 10 | DEBUG = True 11 | TEMPLATE_DEBUG = DEBUG 12 | 13 | ADMINS = ( 14 | ('Yonel Ceruto', 'yonelceruto@gmail.com'), 15 | ) 16 | 17 | MANAGERS = ADMINS 18 | 19 | DATABASES = { 20 | 'default': { 21 | 'ENGINE': 'django.db.backends.sqlite3', 22 | 'NAME': os.path.join(ROOT_DIR, 'data.sqlite3'), 23 | } 24 | } 25 | 26 | # A boolean that specifies if datetimes will be timezone-aware by default or 27 | # not. If this is set to True, Django will use timezone-aware datetimes 28 | # internally. Otherwise, Django will use naive datetimes in local time. 29 | USE_TZ = True 30 | 31 | # Local time zone for this installation. Choices can be found here: 32 | # http://en.wikipedia.org/wiki/List_of_tz_zones_by_name 33 | # although not all choices may be available on all operating systems. 34 | # On Unix systems, a value of None will cause Django to use the same 35 | # timezone as the operating system. 36 | # If running in a Windows environment this must be set to the same as your 37 | # system time zone. 38 | TIME_ZONE = 'UTC' 39 | 40 | # Language code for this installation. All choices can be found here: 41 | # http://www.i18nguy.com/unicode/language-identifiers.html 42 | LANGUAGE_CODE = 'en-us' 43 | 44 | SITE_ID = 1 45 | 46 | # If you set this to False, Django will make some optimizations so as not 47 | # to load the internationalization machinery. 48 | USE_I18N = True 49 | 50 | # If you set this to False, Django will not format dates, numbers and 51 | # calendars according to the current locale 52 | USE_L10N = True 53 | 54 | # Absolute filesystem path to the directory that will hold user-uploaded files. 55 | # Example: "/home/media/media.lawrence.com/media/" 56 | MEDIA_ROOT = '' 57 | 58 | # URL that handles the media served from MEDIA_ROOT. Make sure to use a 59 | # trailing slash. 60 | # Examples: "http://media.lawrence.com/media/", "http://example.com/media/" 61 | MEDIA_URL = '' 62 | 63 | # Absolute path to the directory static files should be collected to. 64 | # Don't put anything in this directory yourself; store your static files 65 | # in apps' "static/" subdirectories and in STATICFILES_DIRS. 66 | # Example: "/home/media/media.lawrence.com/static/" 67 | STATIC_ROOT = '' 68 | 69 | # URL prefix for static files. 70 | # Example: "http://media.lawrence.com/static/" 71 | STATIC_URL = '/static/' 72 | 73 | # URL prefix for admin static files -- CSS, JavaScript and images. 74 | # Make sure to use a trailing slash. 75 | # Examples: "http://foo.com/static/admin/", "/static/admin/". 76 | ADMIN_MEDIA_PREFIX = '/static/admin/' 77 | 78 | # Make this unique, and don't share it with anybody. 79 | SECRET_KEY = '(l*w&8lg1co#vw#3$1#i^!!!tvhiw061%@jm*_-#_o@jv-y^#d' 80 | 81 | MIDDLEWARE = ( 82 | 'django.middleware.security.SecurityMiddleware', 83 | 'django.contrib.sessions.middleware.SessionMiddleware', 84 | 'django.middleware.common.CommonMiddleware', 85 | 'django.middleware.csrf.CsrfViewMiddleware', 86 | 'django.contrib.auth.middleware.AuthenticationMiddleware', 87 | 'django.contrib.messages.middleware.MessageMiddleware', 88 | 'django.middleware.clickjacking.XFrameOptionsMiddleware', 89 | 'django_ajax.middleware.AJAXMiddleware', 90 | ) 91 | 92 | ROOT_URLCONF = 'urls' 93 | 94 | TEMPLATES = [ 95 | { 96 | 'BACKEND': 'django.template.backends.django.DjangoTemplates', 97 | 'DIRS': [os.path.join(ROOT_DIR, 'app/templates')], 98 | 'APP_DIRS': True, 99 | 'OPTIONS': { 100 | 'context_processors': [ 101 | 'django.template.context_processors.debug', 102 | 'django.template.context_processors.request', 103 | 'django.contrib.auth.context_processors.auth', 104 | 'django.contrib.messages.context_processors.messages', 105 | ], 106 | }, 107 | }, 108 | ] 109 | 110 | INSTALLED_APPS = ( 111 | 'app', 112 | 'django.contrib.admin', 113 | 'django.contrib.auth', 114 | 'django.contrib.contenttypes', 115 | 'django.contrib.sessions', 116 | 'django.contrib.messages', 117 | 'django.contrib.staticfiles', 118 | ) 119 | 120 | AUTH_PASSWORD_VALIDATORS = [ 121 | { 122 | 'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator', 123 | }, 124 | { 125 | 'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator', 126 | }, 127 | { 128 | 'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator', 129 | }, 130 | { 131 | 'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator', 132 | }, 133 | ] 134 | 135 | # A sample logging configuration. The only tangible logging 136 | # performed by this configuration is to send an email to 137 | # the site admins on every HTTP 500 error. 138 | # See http://docs.djangoproject.com/en/dev/topics/logging for 139 | # more details on how to customize your logging configuration. 140 | LOGGING = { 141 | 'version': 1, 142 | 'disable_existing_loggers': False, 143 | 'handlers': { 144 | 'mail_admins': { 145 | 'level': 'ERROR', 146 | 'filters': [], 147 | 'class': 'django.utils.log.AdminEmailHandler' 148 | } 149 | }, 150 | 'loggers': { 151 | 'django.request': { 152 | 'handlers': ['mail_admins'], 153 | 'level': 'ERROR', 154 | 'propagate': True, 155 | }, 156 | } 157 | } 158 | 159 | # Only run Jenkins report generation on these apps. 160 | PROJECT_APPS = ('app',) 161 | 162 | # Which Jenkins reports/tasks to run. 163 | JENKINS_TASKS = ('django_jenkins.tasks.run_pylint', 164 | 'django_jenkins.tasks.run_pep8', 165 | 'django_jenkins.tasks.run_pyflakes', 166 | 'django_jenkins.tasks.with_coverage', 167 | 'django_jenkins.tasks.django_tests',) 168 | 169 | # The test runner for the Jenkins command. 170 | JENKINS_TEST_RUNNER = 'django_jenkins.runner.CITestSuiteRunner' 171 | 172 | # django-ajax specific settings 173 | MAX_PER_PAGE = 20 174 | -------------------------------------------------------------------------------- /tests/ajaxmiddleware/urls.py: -------------------------------------------------------------------------------- 1 | from __future__ import absolute_import 2 | from app import views 3 | import django 4 | 5 | try: 6 | try: 7 | from django.conf.urls import patterns, include, url 8 | except ImportError: 9 | from django.conf.urls import include, url 10 | except ImportError: 11 | from django.urls import include, re_path 12 | 13 | if django.VERSION < (1, 8): 14 | urlpatterns = patterns('', 15 | url(r'^ajax/foo$', views.foo_view, name='foo'), 16 | url(r'^ajax/login-required$', views.login_required_view, name='login_required'), 17 | url(r'^ajax/render$', views.render_view, name='render'), 18 | url(r'^ajax/render-class-based-view$', views.SimpleView.as_view(), name='render_class_based_view'), 19 | url(r'^ajax/exception$', views.exception_view, name='exception'), 20 | url(r'^ajax/raise-exception$', views.raise_exception_view, name='raise_exception'), 21 | ) 22 | elif django.VERSION < (4, 0): 23 | urlpatterns = [ 24 | url(r'^ajax/foo$', views.foo_view, name='foo'), 25 | url(r'^ajax/login-required$', views.login_required_view, name='login_required'), 26 | url(r'^ajax/render$', views.render_view, name='render'), 27 | url(r'^ajax/render-class-based-view$', views.SimpleView.as_view(), name='render_class_based_view'), 28 | url(r'^ajax/exception$', views.exception_view, name='exception'), 29 | url(r'^ajax/raise-exception$', views.raise_exception_view, name='raise_exception'), 30 | ] 31 | else: 32 | urlpatterns = [ 33 | re_path(r'^ajax/foo$', views.foo_view, name='foo'), 34 | re_path(r'^ajax/login-required$', views.login_required_view, name='login_required'), 35 | re_path(r'^ajax/render$', views.render_view, name='render'), 36 | re_path(r'^ajax/render-class-based-view$', views.SimpleView.as_view(), name='render_class_based_view'), 37 | re_path(r'^ajax/exception$', views.exception_view, name='exception'), 38 | re_path(r'^ajax/raise-exception$', views.raise_exception_view, name='raise_exception'), 39 | ] 40 | --------------------------------------------------------------------------------