djgentelella 0.3.18__py3-none-any.whl → 0.3.20__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (47) hide show
  1. djgentelella/__init__.py +1 -1
  2. djgentelella/blog/forms.py +3 -3
  3. djgentelella/fields/files.py +125 -0
  4. djgentelella/forms/decorators.py +3 -3
  5. djgentelella/forms/forms.py +153 -33
  6. djgentelella/forms/models.py +2 -2
  7. djgentelella/serializers/selects.py +32 -0
  8. djgentelella/settings.py +1 -1
  9. djgentelella/static/djgentelella.flags.vendors.min.css +1 -1
  10. djgentelella/static/djgentelella.readonly.vendors.min.js +1 -1
  11. djgentelella/static/djgentelella.vendors.header.min.js +1 -1
  12. djgentelella/static/djgentelella.vendors.min.js +3 -2
  13. djgentelella/static/gentelella/css/custom.css +42 -2
  14. djgentelella/static/gentelella/js/base/dateranges_gridslider.js +1 -2
  15. djgentelella/static/gentelella/js/base/fileupload.widget.js +175 -72
  16. djgentelella/static/gentelella/js/base.js +178 -75
  17. djgentelella/static/gentelella/js/datatables.js +20 -3
  18. djgentelella/static/gentelella/js/obj_api_management.js +150 -51
  19. djgentelella/static/gentelella/js/widgets.js +2 -1
  20. djgentelella/static/vendors/flags/1x1/cp.svg +2 -2
  21. djgentelella/static/vendors/flags/1x1/dg.svg +2 -2
  22. djgentelella/static/vendors/flags/1x1/es-ga.svg +2 -2
  23. djgentelella/static/vendors/flags/4x3/ac.svg +2 -2
  24. djgentelella/static/vendors/flags/4x3/ea.svg +2 -2
  25. djgentelella/static/vendors/flags/4x3/es-ct.svg +2 -2
  26. djgentelella/static/vendors/friconix/friconix.js +1 -1
  27. djgentelella/static/vendors/interact/interact.min.js +3 -2
  28. djgentelella/static/vendors/storymapjs/storymap.js +1 -1
  29. djgentelella/templates/forms/as_grid.html +39 -0
  30. djgentelella/templates/forms/as_horizontal.html +36 -0
  31. djgentelella/templates/forms/as_inline.html +38 -0
  32. djgentelella/templates/forms/as_plain.html +33 -0
  33. djgentelella/templates/gentelella/index.html +14 -14
  34. djgentelella/templates/gentelella/widgets/chunkedupload.html +9 -14
  35. djgentelella/templates/gentelella/widgets/file.html +4 -9
  36. djgentelella/tests/__init__.py +2 -1
  37. djgentelella/tests/fields/__init__.py +0 -0
  38. djgentelella/tests/fields/files.py +39 -0
  39. djgentelella/views/select2autocomplete.py +8 -2
  40. djgentelella/widgets/core.py +33 -0
  41. djgentelella/widgets/files.py +37 -8
  42. {djgentelella-0.3.18.dist-info → djgentelella-0.3.20.dist-info}/METADATA +30 -15
  43. {djgentelella-0.3.18.dist-info → djgentelella-0.3.20.dist-info}/RECORD +47 -39
  44. {djgentelella-0.3.18.dist-info → djgentelella-0.3.20.dist-info}/WHEEL +1 -1
  45. {djgentelella-0.3.18.dist-info → djgentelella-0.3.20.dist-info}/AUTHORS +0 -0
  46. {djgentelella-0.3.18.dist-info → djgentelella-0.3.20.dist-info}/LICENSE.txt +0 -0
  47. {djgentelella-0.3.18.dist-info → djgentelella-0.3.20.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,39 @@
1
+ {% if errors %}
2
+ <div class="alert alert-warning d-flex align-items-center" role="alert">
3
+ <svg class="bi flex-shrink-0 me-2" width="24" height="24" role="img" aria-label="Warning:">
4
+ <use xlink:href="#exclamation-triangle-fill"/>
5
+ </svg>
6
+ <div>
7
+ {{ errors }}
8
+ {% if not fields %}
9
+ {% for field in hidden_fields %}{{ field }}{% endfor %}
10
+ {% endif %}
11
+ </div>
12
+ </div>
13
+
14
+ {% endif %}
15
+ {% for column in form.grid %}
16
+ <div class="row">{% for row in column %}
17
+ <div class="col">{% for field in row %}
18
+ <div {% with classes=field.css_classes %}{% if classes %} class="{{ classes }}" {% else %} class="mb-3" {% endif %}
19
+ {% endwith %}>
20
+ {{ errors }}
21
+ {% if field.label %}{{ field.label_tag }}{% endif %}
22
+ {{ field }}
23
+ {% if field.help_text %}
24
+ <div class="feedback text-info" {% if field.auto_id %} id="{{ field.auto_id }}_helptext" {% endif %}>
25
+ {{ field.help_text|safe }}
26
+ </div>
27
+ {% endif %}
28
+ </div>
29
+ {% endfor %}
30
+ </div>
31
+ {% endfor %}
32
+ {% if forloop.last %}
33
+ {% for field in hidden_fields %}{{ field }}{% endfor %}
34
+ {% endif %}
35
+ </div>{% endfor %}
36
+
37
+ {% if not fields and not errors %}
38
+ {% for field in hidden_fields %}{{ field }}{% endfor %}
39
+ {% endif %}
@@ -0,0 +1,36 @@
1
+ {% if errors %}
2
+ <div class="alert alert-warning d-flex align-items-center" role="alert">
3
+ <svg class="bi flex-shrink-0 me-2" width="24" height="24" role="img" aria-label="Warning:">
4
+ <use xlink:href="#exclamation-triangle-fill"/>
5
+ </svg>
6
+ <div>
7
+ {{ errors }}
8
+ {% if not fields %}
9
+ {% for field in hidden_fields %}{{ field }}{% endfor %}
10
+ {% endif %}
11
+ </div>
12
+ </div>
13
+
14
+ {% endif %}
15
+ {% for field, errors in fields %}
16
+ <div {% with classes=field.css_classes %}{% if classes %} class="{{ classes }}" {% else %}class="row form-group mb-3"
17
+ {% endif %}{% endwith %}>
18
+ {{ errors }}
19
+ {% if field.label %}<label for="{{ field.id_for_label }}" class="col-sm-2 col-form-label">{{ field.label }} </label>
20
+ {% endif %}
21
+ <div class="col-sm-10">
22
+ {{ field }}
23
+ {% if field.help_text %}
24
+ <div class="valid-feedback" {% if field.auto_id %} id="{{ field.auto_id }}_helptext" {% endif %}>
25
+ {{ field.help_text|safe }}
26
+ </div>
27
+ {% endif %}
28
+ {% if forloop.last %}
29
+ {% for field in hidden_fields %}{{ field }}{% endfor %}
30
+ {% endif %}
31
+ </div>
32
+ </div>
33
+ {% endfor %}
34
+ {% if not fields and not errors %}
35
+ {% for field in hidden_fields %}{{ field }}{% endfor %}
36
+ {% endif %}
@@ -0,0 +1,38 @@
1
+ {% if errors %}
2
+ <div class="alert alert-warning d-flex align-items-center" role="alert">
3
+ <svg class="bi flex-shrink-0 me-2" width="24" height="24" role="img" aria-label="Warning:">
4
+ <use xlink:href="#exclamation-triangle-fill"/>
5
+ </svg>
6
+ <div>
7
+ {{ errors }}
8
+ {% if not fields %}
9
+ {% for field in hidden_fields %}{{ field }}{% endfor %}
10
+ {% endif %}
11
+ </div>
12
+ </div>
13
+
14
+ {% endif %}
15
+ <div class="row">
16
+ {% for field, errors in fields %}
17
+ <div {% with classes=field.css_classes %}{% if classes %} class="{{ classes }}" {% else %}class="col"
18
+ {% endif %}{% endwith %}>
19
+ {{ errors }}
20
+ {% if field.label %}{{ field.label_tag }}{% endif %}
21
+
22
+ {{ field }}
23
+ {% if field.help_text %}
24
+ {% if forloop.last %}
25
+ {% for field in hidden_fields %}{{ field }}{% endfor %}
26
+ {% endif %}
27
+
28
+ <div class="feedback text-info" {% if field.auto_id %} id="{{ field.auto_id }}_helptext" {% endif %}>
29
+ {{ field.help_text|safe }}
30
+ </div>
31
+ {% endif %}
32
+
33
+ </div>
34
+ {% endfor %}
35
+ {% if not fields and not errors %}
36
+ {% for field in hidden_fields %}{{ field }}{% endfor %}
37
+ {% endif %}
38
+ </div>
@@ -0,0 +1,33 @@
1
+ {% if errors %}
2
+ <div class="alert alert-warning d-flex align-items-center" role="alert">
3
+ <svg class="bi flex-shrink-0 me-2" width="24" height="24" role="img" aria-label="Warning:">
4
+ <use xlink:href="#exclamation-triangle-fill"/>
5
+ </svg>
6
+ <div>
7
+ {{ errors }}
8
+ {% if not fields %}
9
+ {% for field in hidden_fields %}{{ field }}{% endfor %}
10
+ {% endif %}
11
+ </div>
12
+ </div>
13
+
14
+ {% endif %}
15
+ {% for field, errors in fields %}
16
+ <div {% with classes=field.css_classes %}{% if classes %} class="{{ classes }}" {% else %}class="col-12"
17
+ {% endif %}{% endwith %}>
18
+ {{ errors }}
19
+ {% if field.label %}{{ field.label_tag }}{% endif %}
20
+ {{ field }}
21
+ {% if field.help_text %}
22
+ <div class="valid-feedback" {% if field.auto_id %} id="{{ field.auto_id }}_helptext" {% endif %}>
23
+ {{ field.help_text|safe }}
24
+ </div>
25
+ {% endif %}
26
+ {% if forloop.last %}
27
+ {% for field in hidden_fields %}{{ field }}{% endfor %}
28
+ {% endif %}
29
+ </div>
30
+ {% endfor %}
31
+ {% if not fields and not errors %}
32
+ {% for field in hidden_fields %}{{ field }}{% endfor %}
33
+ {% endif %}
@@ -11,22 +11,22 @@
11
11
  {% endblock%}
12
12
  <div class="row">
13
13
 
14
- <div class='col-sm-12 col-md-12 col-lg-12'>
15
- <div class="card">
16
- <div class="card-body">
17
- <div class="card-title titles">
18
- <h2> A form </h2>
19
- </div>
20
- <form action="" method="POST" enctype="multipart/form-data">
21
- {% csrf_token %}
22
- {{form.as_inline}}
23
- <input class="btn btn-success" type="submit" value="Save"/>
24
- </form>
14
+ <div class='col-sm-12 col-md-12 col-lg-12'>
15
+ <div class="card">
16
+ <div class="card-body">
17
+ <div class="card-title titles">
18
+ <h2> A form </h2>
19
+ </div>
20
+ <form action="" method="POST" enctype="multipart/form-data">
21
+ {% csrf_token %}
22
+ {{form}}
23
+ <input class="btn btn-success" type="submit" value="Save"/>
24
+ </form>
25
25
 
26
- </div>
26
+ </div>
27
27
 
28
- </div>
29
- </div>
28
+ </div>
29
+ </div>
30
30
  </div>
31
31
  {% endblock %}
32
32
  {% block js %} {{ form.media }} {% endblock %}
@@ -6,24 +6,19 @@
6
6
  <input class="chunkedupload-input" style="" type="{{ widget.type }}" data-inputtoken="{{ widget.name }}" data-process=".{{ widget.name }}_progress" data-message=".{{ widget.name }}_messages" name="{{ widget.name }}_widget" {% include "gentelella/widgets/attrs.html" %}>
7
7
  </div>
8
8
  <div class="form-control {{ widget.name }}_messages" style="display:none;"></div>
9
- <input type="hidden" name="{{ widget.name }}" value="" />
9
+ <input class="chunkedvalue" type="hidden" name="{{ widget.name }}" value='{{widget.value}}' autocomplete="off"/>
10
10
  <div class="input-group-text fileshow" ><i class="fa fa-eye"></i></div>
11
- {% if widget.value %}
12
- <div id="download_{{ widget.name }}" class="input-group-text bg-danger" >
13
- <a href="{{widget.value.url}}" target="_blank"><i class="fa fa-download text-light"></i></a>
11
+ <div id="download_{{ widget.name }}" class="input-group-text bg-danger" {% if not widget.value %}style="display:none;"{% endif %} >
12
+ <a href="" target="_blank"><i class="fa fa-download text-light"></i></a>
14
13
  </div>
15
- {% endif %}
16
14
  </div>
17
-
18
- {% if widget.value and not widget.required %}
19
- <div class="checkbox">
15
+ <div id="remove_{{ widget.name }}" class="checkbox">
20
16
  <label class="">
21
- <div class="icheckbox_flat-green checked" style="position: relative;">
22
- <input type="checkbox" autocomplete="off" data-widget="CheckboxInput" class="flat" style="position: absolute; opacity: 0;">
17
+ <div class="icheckbox_flat-green" style="position: relative;">
18
+ <input id="id_remove_{{ widget.name }}" type="checkbox" autocomplete="off" data-widget="CheckboxInput" name="remove_{{ widget.name }}_gt" class="flat" style="position: absolute; opacity: 0;">
23
19
  <ins class="iCheck-helper" ></ins></div>
24
- {% trans 'Delete this file' %}
25
-
20
+ {% trans 'Delete this file' %}
26
21
  </label>
27
22
  </div>
28
- {% endif %}
29
- </div>
23
+
24
+ </div>
@@ -7,24 +7,19 @@
7
7
  <input type="{{ widget.type }}" data-inputtoken="{{ widget.name }}" data-process=".{{ widget.name }}_progress" data-message=".{{ widget.name }}_messages" name="{{ widget.name }}_widget" {% include "gentelella/widgets/attrs.html" %}>
8
8
  </div>
9
9
  <div class="form-control {{ widget.name }}_messages" style="display:none;"></div>
10
- <input type="hidden" name="{{ widget.name }}" value="" />
10
+ <input type="hidden" name="{{ widget.name }}" value="{{ widget.value }}" />
11
11
  <div class="input-group-text fileshow" ><i class="fa fa-eye"></i></div>
12
- {% if widget.value %}
13
12
  <div id="download_{{ widget.name }}" class="input-group-text bg-danger" >
14
- <a href="{{widget.value.url}}" target="_blank"><i class="fa fa-download text-light"></i></a>
13
+ <a href="" target="_blank"><i class="fa fa-download text-light"></i></a>
15
14
  </div>
16
- {% endif %}
17
15
  </div>
18
-
19
- {% if widget.value and not widget.required %}
20
- <div class="checkbox">
16
+ <div id="check_{{ widget.name }}" class="checkbox">
21
17
  <label class="">
22
- <div class="icheckbox_flat-green checked" style="position: relative;">
18
+ <div class="icheckbox_flat-green " style="position: relative;">
23
19
  <input type="checkbox" autocomplete="off" data-widget="CheckboxInput" class="flat" style="position: absolute; opacity: 0;">
24
20
  <ins class="iCheck-helper" ></ins></div>
25
21
  {% trans 'Delete this file' %}
26
22
 
27
23
  </label>
28
24
  </div>
29
- {% endif %}
30
25
  </div>
@@ -1,5 +1,6 @@
1
1
  from .Calendar_Test import *
2
+ from .Notification_Test import *
2
3
  from .StoryLine_Test import *
3
4
  from .StoryMap_Test import *
4
5
  from .TimeLine_Test import *
5
- from .Notification_Test import *
6
+ from .fields.files import *
File without changes
@@ -0,0 +1,39 @@
1
+ import base64
2
+
3
+ from django.core.files.base import ContentFile
4
+ from django.test import TestCase
5
+ from rest_framework import serializers
6
+
7
+ from djgentelella.fields.files import GTBase64FileField
8
+
9
+
10
+ class GTBase64FileFieldTestCase(TestCase):
11
+ def test_to_internal_value_with_valid_data(self):
12
+ field = GTBase64FileField()
13
+ data = {
14
+ "name": "test.txt",
15
+ "value": base64.b64encode(b"Test content").decode("utf-8"),
16
+ }
17
+ expected_content = ContentFile(b"Test content")
18
+
19
+ result = field.to_internal_value(data)
20
+
21
+ self.assertEqual(result.read(), expected_content.read())
22
+ self.assertEqual(result.name, expected_content.name)
23
+
24
+ def test_to_internal_value_with_missing_fields(self):
25
+ field = GTBase64FileField()
26
+ data = {"name": "test.txt"}
27
+
28
+ with self.assertRaises(serializers.ValidationError):
29
+ field.to_internal_value(data)
30
+
31
+ def test_to_internal_value_with_invalid_base64(self):
32
+ field = GTBase64FileField()
33
+ data = {
34
+ "name": "test.txt",
35
+ "value": "invalid_base64",
36
+ }
37
+
38
+ with self.assertRaises(serializers.ValidationError):
39
+ field.to_internal_value(data)
@@ -71,14 +71,20 @@ class BaseSelect2View(generics.ListAPIView, viewsets.GenericViewSet):
71
71
  text_wrapper = ''
72
72
  order_by = 'pk'
73
73
 
74
+ def get_filter_suffix(self, fieldname):
75
+ if hasattr(self, f'get_filter_suffix_{fieldname}'):
76
+ return getattr(self, f'get_filter_suffix_{fieldname}')()
77
+ return '__icontains'
78
+
74
79
  def filter_data(self, queryset, qe):
75
80
  filters = None
76
81
  for field in self.fields:
82
+ suffix = self.get_filter_suffix(field)
77
83
  for q in qe:
78
84
  if filters is None:
79
- filters = Q(**{field + '__icontains': q})
85
+ filters = Q(**{field + suffix: q})
80
86
  else:
81
- filters |= Q(**{field + '__icontains': q})
87
+ filters |= Q(**{field + suffix: q})
82
88
  return queryset.filter(filters)
83
89
 
84
90
  def query_get(self, name, default, aslist=False):
@@ -18,6 +18,8 @@ from django.urls import reverse_lazy
18
18
  from django.utils import formats
19
19
  from django.utils.translation import gettext as _
20
20
 
21
+ from djgentelella.models import ChunkedUpload
22
+
21
23
 
22
24
  def update_kwargs(attrs, widget, base_class='form-control '):
23
25
  if attrs is not None:
@@ -117,6 +119,21 @@ class NumberInput(Input):
117
119
  super().__init__(attrs, extraskwargs=extraskwargs)
118
120
 
119
121
 
122
+ class FloatInput(Input):
123
+ input_type = 'number'
124
+ template_name = 'gentelella/widgets/number.html'
125
+
126
+ # min_value y max_value
127
+
128
+ def __init__(self, attrs=None, extraskwargs=True):
129
+ if extraskwargs:
130
+ attrs = update_kwargs(attrs, self.__class__.__name__)
131
+ if 'step' not in attrs:
132
+ attrs['step'] = "0.1"
133
+ attrs['inputmode'] = "decimal"
134
+ super().__init__(attrs, extraskwargs=extraskwargs)
135
+
136
+
120
137
  class EmailInput(Input):
121
138
  input_type = 'email'
122
139
  template_name = 'gentelella/widgets/email.html'
@@ -168,6 +185,22 @@ class FileInput(DJFileInput):
168
185
  attrs['data-done'] = reverse_lazy('upload_file_done')
169
186
  super().__init__(attrs)
170
187
 
188
+ def format_value(self, value):
189
+ """File input never renders a value."""
190
+ return
191
+
192
+ def value_from_datadict(self, data, files, name):
193
+ dev = None
194
+ token = data.get(name)
195
+ tmpupload = ChunkedUpload.objects.filter(upload_id=token).first()
196
+ if tmpupload:
197
+ dev = tmpupload.get_uploaded_file()
198
+ tmpupload.delete()
199
+ return dev
200
+
201
+ def value_omitted_from_data(self, data, files, name):
202
+ return name not in data
203
+
171
204
 
172
205
  class ImageRecordInput(DJFileInput):
173
206
  """
@@ -1,9 +1,16 @@
1
+ import json
2
+ from logging import getLogger
3
+ from pathlib import Path
4
+
1
5
  from django.forms import FileInput
2
6
  from django.urls import reverse_lazy
7
+ from django.utils.safestring import mark_safe
3
8
 
4
9
  from djgentelella.models import ChunkedUpload
5
10
  from djgentelella.widgets.core import update_kwargs
6
11
 
12
+ logger = getLogger('djgentelella')
13
+
7
14
 
8
15
  class FileChunkedUpload(FileInput):
9
16
  input_type = 'file'
@@ -22,17 +29,39 @@ class FileChunkedUpload(FileInput):
22
29
 
23
30
  def format_value(self, value):
24
31
  """File input never renders a value."""
25
- return value
32
+ if value:
33
+ name = Path(value.name).name
34
+ self.value = mark_safe(
35
+ '{"name": "%(name)s", "display_name": "%(display_name)s", "url": "%(url)s" }' % {
36
+ 'name': value.name,
37
+ 'display_name': name,
38
+ 'url': value.url})
39
+ return self.value
40
+ return ''
41
+
42
+ def parse_value(self, value):
43
+ dev = None
44
+ try:
45
+ dev = json.loads(value)
46
+ if not ('url' in dev or 'token' in dev or 'actions' in dev):
47
+ dev = None
48
+ except json.JSONDecodeError as e:
49
+ logger.warning("Json error parsing: " + repr(value))
50
+ return dev
26
51
 
27
52
  def value_from_datadict(self, data, files, name):
28
53
  dev = None
29
- token = data.get(name)
30
- if token == '0':
31
- return False
32
- tmpupload = ChunkedUpload.objects.filter(upload_id=token).first()
33
- if tmpupload:
34
- dev = tmpupload.get_uploaded_file()
35
- tmpupload.delete()
54
+
55
+ token = self.parse_value(data.get(name))
56
+ if token:
57
+ if 'actions' in token and token['actions'] == 'delete':
58
+ return False
59
+ if 'token' in token:
60
+ tmpupload = ChunkedUpload.objects.filter(
61
+ upload_id=token['token']).first()
62
+ if tmpupload:
63
+ dev = tmpupload.get_uploaded_file()
64
+ tmpupload.delete()
36
65
  return dev
37
66
 
38
67
  def value_omitted_from_data(self, data, files, name):
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: djgentelella
3
- Version: 0.3.18
3
+ Version: 0.3.20
4
4
  Summary: Help building extra widgets for forms and speciall methods to render forms in templates
5
5
  Home-page: https://solvosoft.com
6
6
  Author: Luis Zarate Montero
@@ -376,39 +376,54 @@ Requires-Python: >=3.8
376
376
  Description-Content-Type: text/x-rst
377
377
  License-File: LICENSE.txt
378
378
  License-File: AUTHORS
379
- Requires-Dist: django-tree-queries (>=0.11.0)
380
- Requires-Dist: djangoajax (>=3.3)
381
- Requires-Dist: django-markitup (>=4.0.0)
379
+ Requires-Dist: django-tree-queries >=0.11.0
380
+ Requires-Dist: djangoajax >=3.3
381
+ Requires-Dist: django-markitup >=4.0.0
382
382
  Requires-Dist: markdown
383
383
  Requires-Dist: Pillow
384
- Requires-Dist: djangorestframework (>=3.13)
385
- Requires-Dist: django (>=3.2)
386
- Requires-Dist: django-filter (>=22.1)
384
+ Requires-Dist: djangorestframework >=3.13
385
+ Requires-Dist: django >=3.2
386
+ Requires-Dist: django-filter >=22.1
387
387
  Provides-Extra: dev
388
388
  Requires-Dist: pylp ; extra == 'dev'
389
389
  Requires-Dist: pylpconcat ; extra == 'dev'
390
390
  Requires-Dist: css-html-js-minify ; extra == 'dev'
391
391
  Provides-Extra: test
392
- Requires-Dist: selenium (==4.5.0) ; extra == 'test'
393
- Requires-Dist: selenium-screenshot (==2.0.0) ; extra == 'test'
392
+ Requires-Dist: selenium ==4.5.0 ; extra == 'test'
393
+ Requires-Dist: selenium-screenshot ==2.0.0 ; extra == 'test'
394
394
 
395
395
  Django Gentelella widgets
396
396
  ############################
397
397
 
398
+ .. image:: https://img.shields.io/readthedocs/django-gentelella-widgets?label=Read%20the%20Docs&logo=read%20the%20docs&logoColor=white
399
+ :alt: Gentelella documentation
400
+
401
+ .. image:: https://img.shields.io/pypi/pyversions/django
402
+ :alt: Gentelella supported python version
403
+
404
+ .. image:: https://github.com/Solvosoft/django-gentelella-widgets/actions/workflows/django.yml/badge.svg
405
+ :alt: Gentelella test status
406
+
407
+
408
+
409
+ This application is a set of utilities that will make developing applications with django and bootstrap 5 easier, as it provides a set of Javascript libraries such as select2, icheck, datatables and more nicely integrated as widgets or utilities that simplify application creation.
410
+
398
411
  This app helps you to integrate Django apps with `Gentelella <https://colorlib.com/polygon/gentelella/index.html>`_ building extra widgets for forms and speciall methods to render forms in templates.
399
412
 
400
- See `Documentation <https://django-gentelella-widgets.readthedocs.io/>`_
413
+ .. image:: docs/source/_static/readme/logo.png
414
+ :width: 200
415
+ :alt: Gentelella Logo
401
416
 
402
- Installation
417
+ Documentation
403
418
  ________________
404
419
 
405
- Installing from repository (Updated frequently, most of great functionalities are not in pip yet ).
420
+ See `Documentation <https://django-gentelella-widgets.readthedocs.io/>`_
406
421
 
407
- .. code:: bash
422
+ Installation
423
+ ________________
408
424
 
409
- pip install git+https://github.com/luisza/django-gentelella-widgets.git#egg=djgentelella
425
+ Installing from pypi
410
426
 
411
- An stable version on pip, but not all available widget are in this release (new release comming soon)
412
427
 
413
428
  .. code:: bash
414
429