mirror of
				https://github.com/encode/django-rest-framework.git
				synced 2025-10-31 16:07:38 +03:00 
			
		
		
		
	merged with trunk
This commit is contained in:
		
						commit
						c04cb5145c
					
				
							
								
								
									
										1
									
								
								AUTHORS
									
									
									
									
									
								
							
							
						
						
									
										1
									
								
								AUTHORS
									
									
									
									
									
								
							|  | @ -29,6 +29,7 @@ Benoit C <dzen> | |||
| Chris Pickett <bunchesofdonald> | ||||
| Ben Timby <btimby> | ||||
| Michele Lazzeri <michelelazzeri-nextage> | ||||
| Camille Harang <mammique> | ||||
| 
 | ||||
| THANKS TO: | ||||
| 
 | ||||
|  |  | |||
|  | @ -4,11 +4,13 @@ Release Notes | |||
| development | ||||
| ----------- | ||||
| 
 | ||||
| * Saner template variable autoescaping. | ||||
| * Added DjangoModelPermissions class to support `django.contrib.auth` style permissions. | ||||
| * Use `staticfiles` for css files. | ||||
|   - Easier to override.  Won't conflict with customised admin styles (eg grappelli) | ||||
| * Drop implied 'pk' filter if last arg in urlconf is unnamed. | ||||
|   - Too magical.  Explict is better than implicit. | ||||
| * Saner template variable autoescaping. | ||||
| * Tider setup.py | ||||
| * Bugfixes: | ||||
|   - Bug with PerUserThrottling when user contains unicode chars. | ||||
| 
 | ||||
|  |  | |||
|  | @ -1,5 +1,5 @@ | |||
| recursive-include djangorestframework/static *.ico *.txt | ||||
| recursive-include djangorestframework/static *.ico *.txt *.css | ||||
| recursive-include djangorestframework/templates *.txt *.html | ||||
| recursive-include examples .keep *.py *.txt | ||||
| recursive-include docs *.py *.rst *.html *.txt | ||||
| include AUTHORS LICENSE requirements.txt tox.ini | ||||
| include AUTHORS LICENSE CHANGELOG.rst requirements.txt tox.ini | ||||
|  |  | |||
|  | @ -1,7 +1,12 @@ | |||
| Django REST framework | ||||
| ===================== | ||||
| 
 | ||||
| Django REST framework makes it easy to build well-connected, self-describing RESTful Web APIs. | ||||
| **Django REST framework makes it easy to build well-connected, self-describing RESTful Web APIs.** | ||||
| 
 | ||||
| **Author:** Tom Christie.  `Follow me on Twitter <https://twitter.com/_tomchristie>`_. | ||||
| 
 | ||||
| Overview | ||||
| ======== | ||||
| 
 | ||||
| Features: | ||||
| 
 | ||||
|  |  | |||
|  | @ -499,7 +499,7 @@ class PaginatorMixin(object): | |||
|         Constructs a url used for getting the next/previous urls | ||||
|         """ | ||||
|         url = URLObject.parse(self.request.get_full_path()) | ||||
|         url = url.add_query_param('page', page_number) | ||||
|         url = url.set_query_param('page', page_number) | ||||
| 
 | ||||
|         limit = self.get_limit() | ||||
|         if limit != self.limit: | ||||
|  |  | |||
|  | @ -20,6 +20,8 @@ __all__ = ( | |||
|     'PerResourceThrottling' | ||||
| ) | ||||
| 
 | ||||
| SAFE_METHODS = ['GET', 'HEAD', 'OPTIONS'] | ||||
| 
 | ||||
| 
 | ||||
| _403_FORBIDDEN_RESPONSE = ImmediateResponse( | ||||
|     {'detail': 'You do not have permission to access this resource. ' + | ||||
|  | @ -84,8 +86,54 @@ class IsUserOrIsAnonReadOnly(BasePermission): | |||
| 
 | ||||
|     def check_permission(self, user): | ||||
|         if (not user.is_authenticated() and | ||||
|             self.view.method != 'GET' and | ||||
|             self.view.method != 'HEAD'): | ||||
|             self.view.method not in SAFE_METHODS): | ||||
|             raise _403_FORBIDDEN_RESPONSE | ||||
| 
 | ||||
| 
 | ||||
| class DjangoModelPermissions(BasePermission): | ||||
|     """ | ||||
|     The request is authenticated using `django.contrib.auth` permissions. | ||||
|     See: https://docs.djangoproject.com/en/dev/topics/auth/#permissions | ||||
| 
 | ||||
|     It ensures that the user is authenticated, and has the appropriate | ||||
|     `add`/`change`/`delete` permissions on the model. | ||||
| 
 | ||||
|     This permission should only be used on views with a `ModelResource`. | ||||
|     """ | ||||
| 
 | ||||
|     # Map methods into required permission codes. | ||||
|     # Override this if you need to also provide 'read' permissions, | ||||
|     # or if you want to provide custom permission codes. | ||||
|     perms_map = { | ||||
|         'GET': [], | ||||
|         'OPTIONS': [], | ||||
|         'HEAD': [], | ||||
|         'POST': ['%(app_label)s.add_%(model_name)s'], | ||||
|         'PUT': ['%(app_label)s.change_%(model_name)s'], | ||||
|         'PATCH': ['%(app_label)s.change_%(model_name)s'], | ||||
|         'DELETE': ['%(app_label)s.delete_%(model_name)s'], | ||||
|     } | ||||
| 
 | ||||
|     def get_required_permissions(self, method, model_cls): | ||||
|         """ | ||||
|         Given a model and an HTTP method, return the list of permission | ||||
|         codes that the user is required to have. | ||||
|         """ | ||||
|         kwargs = { | ||||
|             'app_label': model_cls._meta.app_label, | ||||
|             'model_name':  model_cls._meta.module_name | ||||
|         } | ||||
|         try: | ||||
|             return [perm % kwargs for perm in self.perms_map[method]] | ||||
|         except KeyError: | ||||
|             ErrorResponse(status.HTTP_405_METHOD_NOT_ALLOWED) | ||||
| 
 | ||||
|     def check_permission(self, user): | ||||
|         method = self.view.method | ||||
|         model_cls = self.view.resource.model | ||||
|         perms = self.get_required_permissions(method, model_cls) | ||||
| 
 | ||||
|         if not user.is_authenticated or not user.has_perms(perms): | ||||
|             raise _403_FORBIDDEN_RESPONSE | ||||
| 
 | ||||
| 
 | ||||
|  |  | |||
|  | @ -162,7 +162,7 @@ class Response(SimpleTemplateResponse): | |||
|         return self.renderers[0] | ||||
| 
 | ||||
| 
 | ||||
| class ImmediateResponse(Response, BaseException): | ||||
| class ImmediateResponse(Response, Exception): | ||||
|     """ | ||||
|     A subclass of :class:`Response` used to abort the current request handling. | ||||
|     """ | ||||
|  |  | |||
|  | @ -1129,6 +1129,58 @@ fieldset.monospace textarea { | |||
|     float: right; | ||||
| } | ||||
| 
 | ||||
| body.login { | ||||
|     background: #eee; | ||||
| } | ||||
| 
 | ||||
| .login #container { | ||||
|     background: white; | ||||
|     border: 1px solid #ccc; | ||||
|     width: 28em; | ||||
|     min-width: 300px; | ||||
|     margin-left: auto; | ||||
|     margin-right: auto; | ||||
|     margin-top: 100px; | ||||
| } | ||||
| 
 | ||||
| .login #content-main { | ||||
|     width: 100%; | ||||
| } | ||||
| 
 | ||||
| .login form { | ||||
|     margin-top: 1em; | ||||
| } | ||||
| 
 | ||||
| .login .form-row { | ||||
|     padding: 4px 0; | ||||
|     float: left; | ||||
|     width: 100%; | ||||
| } | ||||
| 
 | ||||
| .login .form-row label { | ||||
|     float: left; | ||||
|     width: 9em; | ||||
|     padding-right: 0.5em; | ||||
|     line-height: 2em; | ||||
|     text-align: right; | ||||
|     font-size: 1em; | ||||
|     color: #333; | ||||
| } | ||||
| 
 | ||||
| .login .form-row #id_username, .login .form-row #id_password { | ||||
|     width: 14em; | ||||
| } | ||||
| 
 | ||||
| .login span.help { | ||||
|     font-size: 10px; | ||||
|     display: block; | ||||
| } | ||||
| 
 | ||||
| .login .submit-row { | ||||
|     clear: both; | ||||
|     padding: 1em 0 0 9.4em; | ||||
| } | ||||
| 
 | ||||
| /* Overrides specific to REST framework */ | ||||
| 
 | ||||
| #site-name a { | ||||
|  | @ -1147,6 +1199,11 @@ fieldset.monospace textarea { | |||
| } | ||||
| 
 | ||||
| /* Custom styles */ | ||||
| 
 | ||||
| .version { | ||||
|     font-size: 8px; | ||||
| } | ||||
| 
 | ||||
| .form-row { | ||||
|     border-bottom: 0.25em !important; | ||||
| } | ||||
|  |  | |||
|  | @ -1,31 +1,24 @@ | |||
| {% load static %} | ||||
| <html> | ||||
| 
 | ||||
|     <head> | ||||
|     {% if ADMIN_MEDIA_PREFIX %} | ||||
|       <link rel="stylesheet" type="text/css" href='{{ADMIN_MEDIA_PREFIX}}css/base.css'/> | ||||
|       <link rel="stylesheet" type="text/css" href='{{ADMIN_MEDIA_PREFIX}}css/forms.css'/> | ||||
|       <link rel="stylesheet" type="text/css" href='{{ADMIN_MEDIA_PREFIX}}css/login.css' /> | ||||
|     {% else %} | ||||
|       <link rel="stylesheet" type="text/css" href='{{STATIC_URL}}admin/css/base.css'/> | ||||
|       <link rel="stylesheet" type="text/css" href='{{STATIC_URL}}admin/css/forms.css'/> | ||||
|       <link rel="stylesheet" type="text/css" href='{{STATIC_URL}}admin/css/login.css' /> | ||||
|     {% endif %} | ||||
|      <style> | ||||
|      .form-row {border-bottom: 0.25em !important}</style> | ||||
|         <link rel="stylesheet" type="text/css" href='{% get_static_prefix %}css/djangorestframework.css'/> | ||||
|     </head> | ||||
| 
 | ||||
|     <body class="login"> | ||||
| <div id="container"> | ||||
| 
 | ||||
|         <div id="container"> | ||||
| 
 | ||||
|             <div id="header"> | ||||
|                 <div id="branding"> | ||||
|                     <h1 id="site-name">Django REST framework</h1> | ||||
|                 </div> | ||||
|             </div> | ||||
| 
 | ||||
| 
 | ||||
| <div id="content" class="colM"> | ||||
| 
 | ||||
| <div id="content-main"> | ||||
| <form method="post" action="{% url djangorestframework.utils.staticviews.api_login %}" id="login-form"> | ||||
| {% csrf_token %} | ||||
|             <div id="content" class="colM"> | ||||
|                 <div id="content-main"> | ||||
|                     <form method="post" action="{% url djangorestframework.utils.staticviews.api_login %}" id="login-form"> | ||||
|                         {% csrf_token %} | ||||
|                         <div class="form-row"> | ||||
|                             <label for="id_username">Username:</label> {{ form.username }} | ||||
|                         </div> | ||||
|  | @ -36,19 +29,16 @@ | |||
|                         <div class="form-row"> | ||||
|                             <label> </label><input type="submit" value="Log in"> | ||||
|                         </div> | ||||
| </form> | ||||
| 
 | ||||
| <script type="text/javascript"> | ||||
| document.getElementById('id_username').focus() | ||||
| </script> | ||||
| </div> | ||||
| 
 | ||||
| 
 | ||||
|                     </form> | ||||
|                     <script type="text/javascript"> | ||||
|                         document.getElementById('id_username').focus() | ||||
|                     </script> | ||||
|                 </div> | ||||
|                 <br class="clear"> | ||||
|             </div> | ||||
| 
 | ||||
|             <div id="footer"></div> | ||||
| 
 | ||||
| </div> | ||||
| </body> | ||||
|         </div> | ||||
|     </body> | ||||
| </html> | ||||
|  |  | |||
|  | @ -275,3 +275,12 @@ class TestPagination(TestCase): | |||
|         self.assertTrue('foo=bar' in content['next']) | ||||
|         self.assertTrue('another=something' in content['next']) | ||||
|         self.assertTrue('page=2' in content['next']) | ||||
| 
 | ||||
|     def test_duplicate_parameters_are_not_created(self): | ||||
|         """ Regression: ensure duplicate "page" parameters are not added to | ||||
|         paginated URLs. So page 1 should contain ?page=2, not ?page=1&page=2 """ | ||||
|         request = self.req.get('/paginator/?page=1') | ||||
|         response = MockPaginatorView.as_view()(request) | ||||
|         content = json.loads(response.content) | ||||
|         self.assertTrue('page=2' in content['next']) | ||||
|         self.assertFalse('page=1' in content['next']) | ||||
|  |  | |||
|  | @ -1,4 +1,5 @@ | |||
| # Settings for djangorestframework examples project | ||||
| import django | ||||
| import os | ||||
| 
 | ||||
| DEBUG = True | ||||
|  | @ -84,19 +85,17 @@ TEMPLATE_DIRS = ( | |||
|     # Don't forget to use absolute paths, not relative paths. | ||||
| ) | ||||
| 
 | ||||
| # for loading initial data | ||||
| ##SERIALIZATION_MODULES = { | ||||
|   #  'yml': "django.core.serializers.pyyaml" | ||||
| 
 | ||||
| #} | ||||
| 
 | ||||
| if django.VERSION < (1, 3): | ||||
|     staticfiles = 'staticfiles' | ||||
| else: | ||||
|     staticfiles = 'django.contrib.staticfiles' | ||||
| 
 | ||||
| INSTALLED_APPS = ( | ||||
|     'django.contrib.auth', | ||||
|     'django.contrib.contenttypes', | ||||
|     'django.contrib.sessions', | ||||
|     'django.contrib.sites', | ||||
|     'django.contrib.staticfiles', | ||||
|     staticfiles, | ||||
|     'django.contrib.messages', | ||||
| 
 | ||||
|     'djangorestframework', | ||||
|  |  | |||
|  | @ -1,7 +1,10 @@ | |||
| from django.conf.urls.defaults import patterns, include, url | ||||
| from django.conf import settings | ||||
| from django.conf.urls.defaults import patterns, include | ||||
| from sandbox.views import Sandbox | ||||
| from django.contrib.staticfiles.urls import staticfiles_urlpatterns | ||||
| try: | ||||
|     from django.contrib.staticfiles.urls import staticfiles_urlpatterns | ||||
| except ImportError:  # Django <= 1.2 | ||||
|     from staticfiles.urls import staticfiles_urlpatterns | ||||
| 
 | ||||
| 
 | ||||
| urlpatterns = patterns('', | ||||
|     (r'^$', Sandbox.as_view()), | ||||
|  |  | |||
							
								
								
									
										81
									
								
								setup.py
									
									
									
									
									
										
										
										Normal file → Executable file
									
								
							
							
						
						
									
										81
									
								
								setup.py
									
									
									
									
									
										
										
										Normal file → Executable file
									
								
							|  | @ -1,33 +1,70 @@ | |||
| #!/usr/bin/env/python | ||||
| #!/usr/bin/env python | ||||
| # -*- coding: utf-8 -*- | ||||
| 
 | ||||
| from setuptools import setup | ||||
| import re | ||||
| import os | ||||
| import sys | ||||
| 
 | ||||
| import os, re | ||||
| 
 | ||||
| path = os.path.join(os.path.dirname(__file__), 'djangorestframework', '__init__.py') | ||||
| init_py = open(path).read() | ||||
| VERSION = re.match("__version__ = '([^']+)'", init_py).group(1) | ||||
| def get_version(package): | ||||
|     """ | ||||
|     Return package version as listed in `__version__` in `init.py`. | ||||
|     """ | ||||
|     init_py = open(os.path.join(package, '__init__.py')).read() | ||||
|     return re.match("__version__ = ['\"]([^'\"]+)['\"]", init_py).group(1) | ||||
| 
 | ||||
| 
 | ||||
| def get_packages(package): | ||||
|     """ | ||||
|     Return root package and all sub-packages. | ||||
|     """ | ||||
|     return [dirpath | ||||
|             for dirpath, dirnames, filenames in os.walk(package) | ||||
|             if os.path.exists(os.path.join(dirpath, '__init__.py'))] | ||||
| 
 | ||||
| 
 | ||||
| def get_package_data(package): | ||||
|     """ | ||||
|     Return all files under the root package, that are not in a | ||||
|     package themselves. | ||||
|     """ | ||||
|     walk = [(dirpath.replace(package + os.sep, '', 1), filenames) | ||||
|             for dirpath, dirnames, filenames in os.walk(package) | ||||
|             if not os.path.exists(os.path.join(dirpath, '__init__.py'))] | ||||
| 
 | ||||
|     filepaths = [] | ||||
|     for base, filenames in walk: | ||||
|         filepaths.extend([os.path.join(base, filename) | ||||
|                           for filename in filenames]) | ||||
|     return {package: filepaths} | ||||
| 
 | ||||
| 
 | ||||
| version = get_version('djangorestframework') | ||||
| 
 | ||||
| 
 | ||||
| if sys.argv[-1] == 'publish': | ||||
|     os.system("python setup.py sdist upload") | ||||
|     print "You probably want to also tag the version now:" | ||||
|     print "  git tag -a %s -m 'version %s'" % (version, version) | ||||
|     print "  git push --tags" | ||||
|     sys.exit() | ||||
| 
 | ||||
| 
 | ||||
| setup( | ||||
|     name = 'djangorestframework', | ||||
|     version = VERSION, | ||||
|     url = 'http://django-rest-framework.org', | ||||
|     download_url = 'http://pypi.python.org/pypi/djangorestframework/', | ||||
|     license = 'BSD', | ||||
|     description = 'A lightweight REST framework for Django.', | ||||
|     author = 'Tom Christie', | ||||
|     author_email = 'tom@tomchristie.com', | ||||
|     packages = ['djangorestframework', | ||||
|                 'djangorestframework.templatetags', | ||||
|                 'djangorestframework.tests', | ||||
|                 'djangorestframework.runtests', | ||||
|                 'djangorestframework.utils'], | ||||
|     package_dir={'djangorestframework': 'djangorestframework'}, | ||||
|     package_data = {'djangorestframework': ['templates/*', 'static/*']}, | ||||
|     test_suite = 'djangorestframework.runtests.runcoverage.main', | ||||
|     name='djangorestframework', | ||||
|     version=version, | ||||
|     url='http://django-rest-framework.org', | ||||
|     download_url='http://pypi.python.org/pypi/djangorestframework/', | ||||
|     license='BSD', | ||||
|     description='A lightweight REST framework for Django.', | ||||
|     author='Tom Christie', | ||||
|     author_email='tom@tomchristie.com', | ||||
|     packages=get_packages('djangorestframework'), | ||||
|     package_data=get_package_data('djangorestframework'), | ||||
|     test_suite='djangorestframework.runtests.runcoverage.main', | ||||
|     install_requires=['URLObject>=0.6.0'], | ||||
|     classifiers = [ | ||||
|     classifiers=[ | ||||
|         'Development Status :: 4 - Beta', | ||||
|         'Environment :: Web Environment', | ||||
|         'Framework :: Django', | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue
	
	Block a user