├── .gitignore
├── README.md
├── backend
├── __init__.py
├── asgi.py
├── bookstore
│ ├── __init__.py
│ ├── admin.py
│ ├── apps.py
│ ├── forms.py
│ ├── migrations
│ │ ├── 0001_initial.py
│ │ └── __init__.py
│ ├── models.py
│ ├── templates
│ │ └── bookstore
│ │ │ ├── book_list.html
│ │ │ ├── book_table.html
│ │ │ ├── hx
│ │ │ ├── book_detail_hx.html
│ │ │ ├── book_form_hx.html
│ │ │ ├── book_result_hx.html
│ │ │ └── book_update_form_hx.html
│ │ │ └── includes
│ │ │ ├── add_modal.html
│ │ │ ├── detail_modal.html
│ │ │ └── update_modal.html
│ ├── tests.py
│ ├── urls.py
│ └── views.py
├── core
│ ├── __init__.py
│ ├── apps.py
│ ├── models.py
│ ├── static
│ │ ├── css
│ │ │ ├── form.css
│ │ │ └── style.css
│ │ └── img
│ │ │ └── django-logo-negative.png
│ ├── templates
│ │ ├── base.html
│ │ ├── includes
│ │ │ └── nav.html
│ │ └── index.html
│ ├── urls.py
│ └── views.py
├── expense
│ ├── __init__.py
│ ├── admin.py
│ ├── apps.py
│ ├── forms.py
│ ├── migrations
│ │ ├── 0001_initial.py
│ │ └── __init__.py
│ ├── models.py
│ ├── templates
│ │ └── expense
│ │ │ ├── expense_client.html
│ │ │ ├── expense_list.html
│ │ │ ├── expense_table.html
│ │ │ └── hx
│ │ │ ├── expense_detail_hx.html
│ │ │ └── expense_hx.html
│ ├── urls.py
│ └── views.py
├── product
│ ├── __init__.py
│ ├── admin.py
│ ├── apps.py
│ ├── migrations
│ │ ├── 0001_initial.py
│ │ └── __init__.py
│ ├── models.py
│ ├── templates
│ │ └── product
│ │ │ ├── hx
│ │ │ ├── category_modal_form_hx.html
│ │ │ └── product_result_hx.html
│ │ │ ├── includes
│ │ │ └── add_modal.html
│ │ │ ├── product_list.html
│ │ │ └── product_table.html
│ ├── tests.py
│ ├── urls.py
│ └── views.py
├── settings.py
├── state
│ ├── __init__.py
│ ├── apps.py
│ ├── states.py
│ ├── templates
│ │ └── state
│ │ │ ├── hx
│ │ │ ├── state_hx.html
│ │ │ └── uf_hx.html
│ │ │ └── state_list.html
│ ├── urls.py
│ └── views.py
├── urls.py
└── wsgi.py
├── contrib
└── env_gen.py
├── db.json
├── img
├── 01_expense_add.png
├── 02_expense_bulk_update.png
├── 03_expense_update.png
├── 04_expense_delete.png
├── a02_combobox.png
├── a03_tabela.png
├── expense_base.png
├── htmx-2021-07-22-0947.excalidraw
└── htmx.png
├── index.html
├── manage.py
└── requirements.txt
/.gitignore:
--------------------------------------------------------------------------------
1 | # Byte-compiled / optimized / DLL files
2 | __pycache__/
3 | *.py[cod]
4 | *$py.class
5 |
6 | # C extensions
7 | *.so
8 |
9 | # Distribution / packaging
10 | .Python
11 | build/
12 | develop-eggs/
13 | dist/
14 | downloads/
15 | eggs/
16 | .eggs/
17 | lib/
18 | lib64/
19 | parts/
20 | sdist/
21 | var/
22 | wheels/
23 | pip-wheel-metadata/
24 | share/python-wheels/
25 | *.egg-info/
26 | .installed.cfg
27 | *.egg
28 | MANIFEST
29 |
30 | # PyInstaller
31 | # Usually these files are written by a python script from a template
32 | # before PyInstaller builds the exe, so as to inject date/other infos into it.
33 | *.manifest
34 | *.spec
35 |
36 | # Installer logs
37 | pip-log.txt
38 | pip-delete-this-directory.txt
39 |
40 | # Unit test / coverage reports
41 | htmlcov/
42 | .tox/
43 | .nox/
44 | .coverage
45 | .coverage.*
46 | .cache
47 | nosetests.xml
48 | coverage.xml
49 | *.cover
50 | *.py,cover
51 | .hypothesis/
52 | .pytest_cache/
53 |
54 | # Translations
55 | *.mo
56 | *.pot
57 |
58 | # Django stuff:
59 | *.log
60 | local_settings.py
61 | db.sqlite3
62 | db.sqlite3-journal
63 |
64 | # Flask stuff:
65 | instance/
66 | .webassets-cache
67 |
68 | # Scrapy stuff:
69 | .scrapy
70 |
71 | # Sphinx documentation
72 | docs/_build/
73 |
74 | # PyBuilder
75 | target/
76 |
77 | # Jupyter Notebook
78 | .ipynb_checkpoints
79 |
80 | # IPython
81 | profile_default/
82 | ipython_config.py
83 |
84 | # pyenv
85 | .python-version
86 |
87 | # pipenv
88 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
89 | # However, in case of collaboration, if having platform-specific dependencies or dependencies
90 | # having no cross-platform support, pipenv may install dependencies that don't work, or not
91 | # install all needed dependencies.
92 | #Pipfile.lock
93 |
94 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow
95 | __pypackages__/
96 |
97 | # Celery stuff
98 | celerybeat-schedule
99 | celerybeat.pid
100 |
101 | # SageMath parsed files
102 | *.sage.py
103 |
104 | # Environments
105 | .env
106 | .venv
107 | env/
108 | venv/
109 | ENV/
110 | env.bak/
111 | venv.bak/
112 |
113 | # Spyder project settings
114 | .spyderproject
115 | .spyproject
116 |
117 | # Rope project settings
118 | .ropeproject
119 |
120 | # mkdocs documentation
121 | /site
122 |
123 | # mypy
124 | .mypy_cache/
125 | .dmypy.json
126 | dmypy.json
127 |
128 | # Pyre type checker
129 | .pyre/
130 |
131 | .DS_Store
132 |
133 | media/
134 | staticfiles/
135 | .idea
136 | .ipynb_checkpoints/
137 | .vscode
138 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # django-htmx-tutorial
2 |
3 | Tutorial sobre como trabalhar com [Django](https://www.djangoproject.com/) e [htmx](https://htmx.org/).
4 |
5 | 
6 |
7 | ## Este projeto foi feito com:
8 |
9 | * [Python 3.9.6](https://www.python.org/)
10 | * [Django 3.2.*](https://www.djangoproject.com/)
11 | * [htmx](https://htmx.org/)
12 |
13 | ## Como rodar o projeto?
14 |
15 | * Clone esse repositório.
16 | * Crie um virtualenv com Python 3.
17 | * Ative o virtualenv.
18 | * Instale as dependências.
19 | * Rode as migrações.
20 |
21 | ```
22 | git clone https://github.com/rg3915/django-htmx-tutorial.git
23 | cd django-htmx-tutorial
24 | python -m venv .venv
25 | source .venv/bin/activate
26 | pip install -r requirements.txt
27 | python contrib/env_gen.py
28 | python manage.py migrate
29 | python manage.py createsuperuser --username="admin" --email=""
30 | python manage.py runserver
31 | ```
32 |
33 | ## Exemplos
34 |
35 | * [Filtrar várias tabelas com um clique](#filtrar-v%C3%A1rias-tabelas-com-um-clique)
36 |
37 |
38 |
39 |
40 | * [Filtrar com dropdowns dependentes](#filtrar-com-dropdowns-dependentes)
41 |
42 |
43 |
44 |
45 | * [Adicionar itens](#adicionar-itens)
46 |
47 |
48 |
49 |
50 | * [Pagar (editar) vários itens (Bulk Update)](#pagar-editar-vários-itens-bulk-update)
51 |
52 |
53 |
54 |
55 | * [Editar um item](#editar-um-item)
56 |
57 |
58 |
59 |
60 | * [Deletar um item](#deletar-um-item)
61 |
62 |
63 |
64 |
65 | * [client-side-templates](#client-side-templates)
66 |
67 | * [Bookstore (modal)](#bookstore)
68 |
69 | * [Like e unlike](#like-e-unlike)
70 |
71 | * [Criar categoria](#criar-uma-nova-categoria)
72 |
73 | * [Trocar de categoria](#trocar-a-categoria)
74 |
75 |
76 | ## Passo a passo
77 |
78 | ### Clonando o projeto base
79 |
80 | ```
81 | git clone https://github.com/rg3915/django-htmx-tutorial.git
82 | cd django-htmx-tutorial
83 | git checkout passo-a-passo
84 |
85 | python -m venv .venv
86 | source .venv/bin/activate
87 |
88 | pip install -U pip
89 | pip install -r requirements.txt
90 | pip install ipdb
91 |
92 | python contrib/env_gen.py
93 |
94 | python manage.py migrate
95 | python manage.py createsuperuser --username="admin" --email=""
96 | ```
97 |
98 | Vamos editar:
99 |
100 | * base.html
101 | * nav.html
102 | * index.html
103 |
104 |
105 | Em `base.html` escreva
106 |
107 | ```html
108 |
109 |
110 |
111 |
112 | ```
113 |
114 | Em `nav.html` escreva
115 |
116 | ```html
117 |
Região | 221 |
---|
230 | {{ region.1 }} 231 | | 232 |
Estados | 243 |
---|
Despesas CRUD com SPA
678 | ``` 679 | 680 | Descomente `urls.py` 681 | 682 | ```python 683 | path('expense/', include('backend.expense.urls', namespace='expense')), 684 | ``` 685 | 686 | 687 | --- 688 | 689 | ### Pagar (editar) vários itens (Bulk Update) 690 | 691 |  692 | 693 | Vamos editar: 694 | 695 | * views.py 696 | * urls.py 697 | * expense_list.html 698 | 699 | 700 | Escreva o `expense/views.py` 701 | 702 | ```python 703 | @require_http_methods(['POST']) 704 | def expense_paid(request): 705 | ids = request.POST.getlist('ids') 706 | 707 | # Edita as despesas selecionadas. 708 | Expense.objects.filter(id__in=ids).update(paid=True) 709 | 710 | # Retorna todas as despesas novamente. 711 | expenses = Expense.objects.all() 712 | 713 | context = {'object_list': expenses} 714 | return render(request, 'expense/expense_table.html', context) 715 | 716 | 717 | @require_http_methods(['POST']) 718 | def expense_no_paid(request): 719 | ids = request.POST.getlist('ids') 720 | 721 | # Edita as despesas selecionadas. 722 | Expense.objects.filter(id__in=ids).update(paid=False) 723 | 724 | # Retorna todas as despesas novamente. 725 | expenses = Expense.objects.all() 726 | 727 | context = {'object_list': expenses} 728 | return render(request, 'expense/expense_table.html', context) 729 | ``` 730 | 731 | Escreva o `expense/urls.py` 732 | 733 | ```python 734 | path('expense/paid/', v.expense_paid, name='expense_paid'), 735 | path('expense/no-paid/', v.expense_no_paid, name='expense_no_paid'), 736 | ``` 737 | 738 | Escreva o `expense/expense_list.html` 739 | 740 | ```html 741 | 742 | 757 | ``` 758 | 759 | --- 760 | 761 | ### Editar um item 762 | 763 |  764 | 765 | Vamos editar: 766 | 767 | * views.py 768 | * urls.py 769 | * hx/expense_hx.html 770 | * hx/expense_detail.html 771 | 772 | 773 | Escreva o `expense/views.py` 774 | 775 | ```python 776 | # expense/views.py 777 | def expense_detail(request, pk): 778 | template_name = 'expense/hx/expense_detail.html' 779 | obj = Expense.objects.get(pk=pk) 780 | form = ExpenseForm(request.POST or None, instance=obj) 781 | 782 | context = {'object': obj, 'form': form} 783 | return render(request, template_name, context) 784 | 785 | 786 | def expense_update(request, pk): 787 | template_name = 'expense/hx/expense_hx.html' 788 | obj = Expense.objects.get(pk=pk) 789 | form = ExpenseForm(request.POST or None, instance=obj) 790 | context = {'object': obj} 791 | 792 | if request.method == 'POST': 793 | if form.is_valid(): 794 | form.save() 795 | 796 | return render(request, template_name, context) 797 | ``` 798 | 799 | 800 | Escreva o `expense/urls.py` 801 | 802 | ```python 803 | # expense/urls.py 804 | path('Descrição | 975 |Valor | 976 |Pago | 977 |
---|---|---|
{ description } | 986 |{ value } | 987 |988 | 989 | 990 | { #paid } { /paid } 991 | { ^paid } { /paid } 992 | | 993 |
Descrição | 1104 |Valor | 1105 |Pago | 1106 |
---|---|---|
{{ description }} | 1115 |{{ value }} | 1116 |1117 | 1118 | 1119 | {{ #paid }} {{ /paid }} 1120 | {{ ^paid }} {{ /paid }} 1121 | | 1122 |
Título | 1298 |Autor | 1299 |Ações | 1300 |
---|
Título 1525 | {{ object.title }} 1526 |
1527 |Autor 1528 | {{ object.author|default:'---' }} 1529 |
1530 |Produto | 2089 |Categoria | 2090 |
---|