Skip to content

Commit

Permalink
Merge pull request #8 from ChanTsune/Dev
Browse files Browse the repository at this point in the history
Release 1.0
  • Loading branch information
ChanTsune authored Jul 3, 2019
2 parents fe6fc68 + e55416c commit a400e1f
Show file tree
Hide file tree
Showing 19 changed files with 507 additions and 38 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -103,3 +103,4 @@ venv.bak/
# mypy
.mypy_cache/
.vscode/
.DS_Store
217 changes: 211 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -222,13 +222,32 @@ These information is obtained using [user-agents](https://github.com/selwin/pyth

### Access Mixins

#### AllowContentTypeMixin

Restrict the content type of http request.

```py
from django.views.generic import TemplateView
from django_boost.views.mixins import AllowContentTypeMixin

class PostView(AllowContentTypeMixin, TemplateView):
allowed_content_types = ["application/xml"]
template_name = "path/to/template"

```
Restrict request based on `Content-Type` of http header.

If the content type is not allowed, http415 response will be returned.
You can disable restrictions by specifying `strictly = False`


#### ReAuthenticationRequiredMixin

```py
from django.views.generic import TemplateView
from django_boost.views.mixins import ReAuthenticationRequiredMixin

class RecentLogin(ReAuthenticationRequiredMixin,TemplateView):
class RecentLogin(ReAuthenticationRequiredMixin, TemplateView):
template_name = "mypage.html"
auth_unnecessary = 3600
```
Expand Down Expand Up @@ -294,17 +313,68 @@ You can change the query string parameter name by changing `redirect_field_name`

#### UserAgentMixin
```py
from django.views.generic import TemplateView
from django_boost.views.generic import TemplateView
from django_boost.views.mixins import UserAgentMixin

class SameView(UserAgentMixin,TemplateView):
class SameView(UserAgentMixin, TemplateView):
template_name = "default_template"
pc_template_name = "pc_template.html"
tablet_template_name = "tablet_template.html"
mobile_template_name = "mobile_template.html"
```

Switch the template file to be displayed by user agent.
Assign `user_agent` attribute to` self.request` and
switch the template file to be displayed by user agent.

If the user agent can not be determined, the template specified in `template_name` will be used.
`pc_template_name`,`tablet_template_name`,`mobile_template_name` has no arms, but` template_name` is required.

#### JsonRequestMixin
A specialized mixin for `AllowContentTypeMixin` for json.

```py
from django.views.generic import TemplateView
from django_boost.views.mixins import JsonRequestMixin

class PostView(JsonRequestMixin, TemplateView):
template_name = "path/to/template"

def get_context_data(self,**kwargs):
posted_data = self.json
# {"send" : "from cliant"}
return posted_data
```

You can access the dictionary object parsed from the Json string sent by the client in `self.json`

If you use for the purpose of API `JsonView` below is recommended.


### ResponseMixin

#### JsonResponseMixin
Returns the response in Json format

```py
from django.views.generic import TemplateView
from django_boost.views.mixins import JsonResponseMixin

class JsonResponseView(JsonResponseMixin, TemplateView):
extra_context = {"context" : "..."}

def get_context_data(self,**kwargs):
context = {}
context.update(super().get_context_data(**kwargs))
return context

```
The usage of `extra_context` and` get_context_data` is basically the same as `TemplateView`.
The difference is that `TemplateView` is passed directly to the template context, whereas` JsonResponseMixin` is a direct response.


Specify `strictly = True` if you want to limit the Content-Type to Json only.

If you use for the purpose of API `JsonView` below is recommended.

### Form Mixin

Expand Down Expand Up @@ -341,16 +411,58 @@ class CustomerSearchView(FormView):

### GenericView

#### Extended Views

```py
from django_boost.views.generic import View

class YourView(View):

def setup(self, request, *args, **kwargs):
super().setup(request, *args, **kwargs)
## some process before view process

## For example, add attribute to view class

def after_view_process(self, request, response, *args, **kwargs):
super().after_view_process(request, response, *args, **kwargs)
## some process after view process

## For example, add http headers to the response

return response

```
django_boost generic view (
`CreateView`, `DeleteView`, `DetailView`, `FormView`, `ListView`, `TemplateView`, `UpdateView`, `View`) classes has `setup` and `after_view_process` method, These are called before and after processing of View respectively. `setup` method is same as the method added in Django 2.2 .

#### JsonView
`JsonResponseMixin``JsonRequestMixin`を継承したgeneric view class です。
```py
from django_boost.views.generic import JsonView

class SameAPIView(JsonView):

def get_context_data(self,**kwargs):
return self.json
```

In the above example, we just return the sent Json string as it is.


#### ModelCRUDViews

Provides easy creation of CRUDViews linked to model.

`views.py`
```py
from django_boost.views.generic import ModelCRUDViews

class CustomerViews(ModelCRUDViews):
model = Customer
```

`urls.py`
```py
from django.urls import path, include
from . import views
Expand All @@ -359,6 +471,38 @@ urlpatterns = [
path('views/',include(views.CustomerViews().urls)),
]
```
In the template you can use as follows.

```html+django
{% url 'customer:list' %}
{% url 'customer:create' %}
{% url 'customer:detail' %}
{% url 'customer:update' %}
{% url 'customer:delete' %}
```
The name of the URL is defined under the namespace of the lower-cased model class name.

###### Case of Namespaced
`urls.py`
```py
from django.urls import path, include
from . import views

app_name = "myapp"
urlpatterns = [
path('views/',include(views.CustomerViews(app_name="myapp:customer").urls)),
]

```

In the template you can use as follows.
```html+django
{% url 'myapp:customer:list' %}
{% url 'myapp:customer:create' %}
{% url 'myapp:customer:detail' %}
{% url 'myapp:customer:update' %}
{% url 'myapp:customer:delete' %}
```

### Template Tags

Expand Down Expand Up @@ -424,7 +568,68 @@ Replace the query string of the current page URL with the argument.
{# case of current page's query string is `?id=2`#}
{% replace_parameters request 'id' 1 'age' 20 %}
{# The result of replacing is `?id=2&age=20` #}
{# The result of replacing is `?id=1&age=20` #}
```
Useful for pagination.

## utilty functions

### loop utils

#### loopfirst

Yield True when the first element of the given iterator object, False otherwise.

```py
from django_boost.utils.functions import loopfirst


for is_first, v in loopfirst(range(5)):
print(is_first, v)

# True 0
# False 1
# False 2
# False 3
# False 4
```


#### looplast

Yield True when the last element of the given iterator object, False otherwise.

```py
from django_boost.utils.functions import looplast


for is_last, v in looplast(range(5)):
print(is_last, v)

# False 0
# False 1
# False 2
# False 3
# True 4
```

#### loopfirstlast

A function combining `firstloop` and` lastloop`.

Yield True if the first and last element of the iterator object, False otherwise.

```py
from django_boost.utils.functions import loopfirstlast


for first_or_last, v in loopfirstlast(range(5)):
print(first_or_last, v)

# True 0
# False 1
# False 2
# False 3
# True 4
```
Useful for pagination.
5 changes: 5 additions & 0 deletions django_boost/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
from django.utils.version import get_version

VERSION = (1, 0, 0, 'final', 0)

__version__ = get_version(VERSION)
5 changes: 5 additions & 0 deletions django_boost/forms/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
from django_boost.forms.fields import ColorCodeField
from django_boost.forms.mixins import FormUserKwargsMixin
from django_boost.forms.widgets import ColorInput

__all__ = ("ColorCodeField", "ColorInput", "FormUserKwargsMixin")
1 change: 0 additions & 1 deletion django_boost/middleware/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@ class RedirectCorrectHostnameMiddleware(MiddlewareMixin):
if requested hostname and settings.CORRECT_HOST does not match.
"""


conditions = not settings.DEBUG and hasattr(settings, 'CORRECT_HOST')

def __call__(self, request):
Expand Down
58 changes: 58 additions & 0 deletions django_boost/tests.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,61 @@
from django.core.exceptions import ValidationError
from django.test import TestCase

from django_boost.utils.functions import loopfirst, loopfirstlast, looplast
from django_boost.validators import (
validate_color_code, validate_json, validate_uuid4)
# Create your tests here.


class UtilFunctionTest(TestCase):

test_list0 = []
test_list1 = [0]
test_list2 = [0, 1]
test_list3 = [0, 1, 2]

def test_loopfirst(self):
collect = [True, False, False]
for is_first, v in loopfirst(self.test_list0):
self.assertEqual(collect[v], is_first)
for is_first, v in loopfirst(self.test_list1):
self.assertEqual(collect[v], is_first)
for is_first, v in loopfirst(self.test_list2):
self.assertEqual(collect[v], is_first)
for is_first, v in loopfirst(self.test_list3):
self.assertEqual(collect[v], is_first)

def test_looplast(self):
for is_last, v in looplast(self.test_list0):
self.assertEqual([True][v], is_last)
for is_last, v in looplast(self.test_list1):
self.assertEqual([True][v], is_last)
for is_last, v in looplast(self.test_list2):
self.assertEqual([False, True][v], is_last)
for is_last, v in looplast(self.test_list3):
self.assertEqual([False, False, True][v], is_last)

def test_loopfirstlast(self):
for is_first_or_last, v in loopfirstlast(self.test_list0):
self.assertEqual([True][v], is_first_or_last)
for is_first_or_last, v in loopfirstlast(self.test_list1):
self.assertEqual([True][v], is_first_or_last)
for is_first_or_last, v in loopfirstlast(self.test_list2):
self.assertEqual([True, True][v], is_first_or_last)
for is_first_or_last, v in loopfirstlast(self.test_list3):
self.assertEqual([True, False, True][v], is_first_or_last)


class ValidatorTest(TestCase):

def test_validate_color_code(self):
with self.assertRaises(ValidationError):
validate_color_code("00FF11")

def test_validate_json(self):
with self.assertRaises(ValidationError):
validate_json('{"a":"apple",}')

def test_validate_uuid4(self):
with self.assertRaises(ValidationError):
validate_uuid4("59cF05e3-fb29-4be8-af18-da9c94b1964d")
Loading

0 comments on commit a400e1f

Please sign in to comment.