Skip to content

Commit a400e1f

Browse files
authored
Merge pull request #8 from ChanTsune/Dev
Release 1.0
2 parents fe6fc68 + e55416c commit a400e1f

File tree

19 files changed

+507
-38
lines changed

19 files changed

+507
-38
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,3 +103,4 @@ venv.bak/
103103
# mypy
104104
.mypy_cache/
105105
.vscode/
106+
.DS_Store

README.md

Lines changed: 211 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -222,13 +222,32 @@ These information is obtained using [user-agents](https://github.com/selwin/pyth
222222

223223
### Access Mixins
224224

225+
#### AllowContentTypeMixin
226+
227+
Restrict the content type of http request.
228+
229+
```py
230+
from django.views.generic import TemplateView
231+
from django_boost.views.mixins import AllowContentTypeMixin
232+
233+
class PostView(AllowContentTypeMixin, TemplateView):
234+
allowed_content_types = ["application/xml"]
235+
template_name = "path/to/template"
236+
237+
```
238+
Restrict request based on `Content-Type` of http header.
239+
240+
If the content type is not allowed, http415 response will be returned.
241+
You can disable restrictions by specifying `strictly = False`
242+
243+
225244
#### ReAuthenticationRequiredMixin
226245

227246
```py
228247
from django.views.generic import TemplateView
229248
from django_boost.views.mixins import ReAuthenticationRequiredMixin
230249

231-
class RecentLogin(ReAuthenticationRequiredMixin,TemplateView):
250+
class RecentLogin(ReAuthenticationRequiredMixin, TemplateView):
232251
template_name = "mypage.html"
233252
auth_unnecessary = 3600
234253
```
@@ -294,17 +313,68 @@ You can change the query string parameter name by changing `redirect_field_name`
294313

295314
#### UserAgentMixin
296315
```py
297-
from django.views.generic import TemplateView
316+
from django_boost.views.generic import TemplateView
298317
from django_boost.views.mixins import UserAgentMixin
299318

300-
class SameView(UserAgentMixin,TemplateView):
319+
class SameView(UserAgentMixin, TemplateView):
320+
template_name = "default_template"
301321
pc_template_name = "pc_template.html"
302322
tablet_template_name = "tablet_template.html"
303323
mobile_template_name = "mobile_template.html"
304324
```
305325

306-
Switch the template file to be displayed by user agent.
326+
Assign `user_agent` attribute to` self.request` and
327+
switch the template file to be displayed by user agent.
328+
329+
If the user agent can not be determined, the template specified in `template_name` will be used.
330+
`pc_template_name`,`tablet_template_name`,`mobile_template_name` has no arms, but` template_name` is required.
331+
332+
#### JsonRequestMixin
333+
A specialized mixin for `AllowContentTypeMixin` for json.
334+
335+
```py
336+
from django.views.generic import TemplateView
337+
from django_boost.views.mixins import JsonRequestMixin
338+
339+
class PostView(JsonRequestMixin, TemplateView):
340+
template_name = "path/to/template"
341+
342+
def get_context_data(self,**kwargs):
343+
posted_data = self.json
344+
# {"send" : "from cliant"}
345+
return posted_data
346+
```
347+
348+
You can access the dictionary object parsed from the Json string sent by the client in `self.json`
349+
350+
If you use for the purpose of API `JsonView` below is recommended.
351+
352+
353+
### ResponseMixin
354+
355+
#### JsonResponseMixin
356+
Returns the response in Json format
357+
358+
```py
359+
from django.views.generic import TemplateView
360+
from django_boost.views.mixins import JsonResponseMixin
361+
362+
class JsonResponseView(JsonResponseMixin, TemplateView):
363+
extra_context = {"context" : "..."}
364+
365+
def get_context_data(self,**kwargs):
366+
context = {}
367+
context.update(super().get_context_data(**kwargs))
368+
return context
369+
370+
```
371+
The usage of `extra_context` and` get_context_data` is basically the same as `TemplateView`.
372+
The difference is that `TemplateView` is passed directly to the template context, whereas` JsonResponseMixin` is a direct response.
373+
307374

375+
Specify `strictly = True` if you want to limit the Content-Type to Json only.
376+
377+
If you use for the purpose of API `JsonView` below is recommended.
308378

309379
### Form Mixin
310380

@@ -341,16 +411,58 @@ class CustomerSearchView(FormView):
341411

342412
### GenericView
343413

414+
#### Extended Views
415+
416+
```py
417+
from django_boost.views.generic import View
418+
419+
class YourView(View):
420+
421+
def setup(self, request, *args, **kwargs):
422+
super().setup(request, *args, **kwargs)
423+
## some process before view process
424+
425+
## For example, add attribute to view class
426+
427+
def after_view_process(self, request, response, *args, **kwargs):
428+
super().after_view_process(request, response, *args, **kwargs)
429+
## some process after view process
430+
431+
## For example, add http headers to the response
432+
433+
return response
434+
435+
```
436+
django_boost generic view (
437+
`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 .
438+
439+
#### JsonView
440+
`JsonResponseMixin``JsonRequestMixin`を継承したgeneric view class です。
441+
```py
442+
from django_boost.views.generic import JsonView
443+
444+
class SameAPIView(JsonView):
445+
446+
def get_context_data(self,**kwargs):
447+
return self.json
448+
```
449+
450+
In the above example, we just return the sent Json string as it is.
451+
452+
344453
#### ModelCRUDViews
345454

346455
Provides easy creation of CRUDViews linked to model.
456+
457+
`views.py`
347458
```py
348459
from django_boost.views.generic import ModelCRUDViews
349460

350461
class CustomerViews(ModelCRUDViews):
351462
model = Customer
352463
```
353464

465+
`urls.py`
354466
```py
355467
from django.urls import path, include
356468
from . import views
@@ -359,6 +471,38 @@ urlpatterns = [
359471
path('views/',include(views.CustomerViews().urls)),
360472
]
361473
```
474+
In the template you can use as follows.
475+
476+
```html+django
477+
{% url 'customer:list' %}
478+
{% url 'customer:create' %}
479+
{% url 'customer:detail' %}
480+
{% url 'customer:update' %}
481+
{% url 'customer:delete' %}
482+
```
483+
The name of the URL is defined under the namespace of the lower-cased model class name.
484+
485+
###### Case of Namespaced
486+
`urls.py`
487+
```py
488+
from django.urls import path, include
489+
from . import views
490+
491+
app_name = "myapp"
492+
urlpatterns = [
493+
path('views/',include(views.CustomerViews(app_name="myapp:customer").urls)),
494+
]
495+
496+
```
497+
498+
In the template you can use as follows.
499+
```html+django
500+
{% url 'myapp:customer:list' %}
501+
{% url 'myapp:customer:create' %}
502+
{% url 'myapp:customer:detail' %}
503+
{% url 'myapp:customer:update' %}
504+
{% url 'myapp:customer:delete' %}
505+
```
362506

363507
### Template Tags
364508

@@ -424,7 +568,68 @@ Replace the query string of the current page URL with the argument.
424568
{# case of current page's query string is `?id=2`#}
425569
{% replace_parameters request 'id' 1 'age' 20 %}
426570
427-
{# The result of replacing is `?id=2&age=20` #}
571+
{# The result of replacing is `?id=1&age=20` #}
572+
573+
```
574+
Useful for pagination.
575+
576+
## utilty functions
577+
578+
### loop utils
579+
580+
#### loopfirst
581+
582+
Yield True when the first element of the given iterator object, False otherwise.
583+
584+
```py
585+
from django_boost.utils.functions import loopfirst
586+
587+
588+
for is_first, v in loopfirst(range(5)):
589+
print(is_first, v)
590+
591+
# True 0
592+
# False 1
593+
# False 2
594+
# False 3
595+
# False 4
596+
```
597+
598+
599+
#### looplast
600+
601+
Yield True when the last element of the given iterator object, False otherwise.
602+
603+
```py
604+
from django_boost.utils.functions import looplast
605+
606+
607+
for is_last, v in looplast(range(5)):
608+
print(is_last, v)
609+
610+
# False 0
611+
# False 1
612+
# False 2
613+
# False 3
614+
# True 4
615+
```
616+
617+
#### loopfirstlast
618+
619+
A function combining `firstloop` and` lastloop`.
620+
621+
Yield True if the first and last element of the iterator object, False otherwise.
622+
623+
```py
624+
from django_boost.utils.functions import loopfirstlast
625+
626+
627+
for first_or_last, v in loopfirstlast(range(5)):
628+
print(first_or_last, v)
428629

630+
# True 0
631+
# False 1
632+
# False 2
633+
# False 3
634+
# True 4
429635
```
430-
Useful for pagination.

django_boost/__init__.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
from django.utils.version import get_version
2+
3+
VERSION = (1, 0, 0, 'final', 0)
4+
5+
__version__ = get_version(VERSION)

django_boost/forms/__init__.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
from django_boost.forms.fields import ColorCodeField
2+
from django_boost.forms.mixins import FormUserKwargsMixin
3+
from django_boost.forms.widgets import ColorInput
4+
5+
__all__ = ("ColorCodeField", "ColorInput", "FormUserKwargsMixin")

django_boost/middleware/__init__.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,6 @@ class RedirectCorrectHostnameMiddleware(MiddlewareMixin):
1515
if requested hostname and settings.CORRECT_HOST does not match.
1616
"""
1717

18-
1918
conditions = not settings.DEBUG and hasattr(settings, 'CORRECT_HOST')
2019

2120
def __call__(self, request):

django_boost/tests.py

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,61 @@
1+
from django.core.exceptions import ValidationError
12
from django.test import TestCase
23

4+
from django_boost.utils.functions import loopfirst, loopfirstlast, looplast
5+
from django_boost.validators import (
6+
validate_color_code, validate_json, validate_uuid4)
37
# Create your tests here.
8+
9+
10+
class UtilFunctionTest(TestCase):
11+
12+
test_list0 = []
13+
test_list1 = [0]
14+
test_list2 = [0, 1]
15+
test_list3 = [0, 1, 2]
16+
17+
def test_loopfirst(self):
18+
collect = [True, False, False]
19+
for is_first, v in loopfirst(self.test_list0):
20+
self.assertEqual(collect[v], is_first)
21+
for is_first, v in loopfirst(self.test_list1):
22+
self.assertEqual(collect[v], is_first)
23+
for is_first, v in loopfirst(self.test_list2):
24+
self.assertEqual(collect[v], is_first)
25+
for is_first, v in loopfirst(self.test_list3):
26+
self.assertEqual(collect[v], is_first)
27+
28+
def test_looplast(self):
29+
for is_last, v in looplast(self.test_list0):
30+
self.assertEqual([True][v], is_last)
31+
for is_last, v in looplast(self.test_list1):
32+
self.assertEqual([True][v], is_last)
33+
for is_last, v in looplast(self.test_list2):
34+
self.assertEqual([False, True][v], is_last)
35+
for is_last, v in looplast(self.test_list3):
36+
self.assertEqual([False, False, True][v], is_last)
37+
38+
def test_loopfirstlast(self):
39+
for is_first_or_last, v in loopfirstlast(self.test_list0):
40+
self.assertEqual([True][v], is_first_or_last)
41+
for is_first_or_last, v in loopfirstlast(self.test_list1):
42+
self.assertEqual([True][v], is_first_or_last)
43+
for is_first_or_last, v in loopfirstlast(self.test_list2):
44+
self.assertEqual([True, True][v], is_first_or_last)
45+
for is_first_or_last, v in loopfirstlast(self.test_list3):
46+
self.assertEqual([True, False, True][v], is_first_or_last)
47+
48+
49+
class ValidatorTest(TestCase):
50+
51+
def test_validate_color_code(self):
52+
with self.assertRaises(ValidationError):
53+
validate_color_code("00FF11")
54+
55+
def test_validate_json(self):
56+
with self.assertRaises(ValidationError):
57+
validate_json('{"a":"apple",}')
58+
59+
def test_validate_uuid4(self):
60+
with self.assertRaises(ValidationError):
61+
validate_uuid4("59cF05e3-fb29-4be8-af18-da9c94b1964d")

0 commit comments

Comments
 (0)