Skip to content

Commit

Permalink
wip crud html
Browse files Browse the repository at this point in the history
  • Loading branch information
Tobi-De committed Dec 20, 2023
1 parent 9e9a322 commit 7eb12f0
Show file tree
Hide file tree
Showing 13 changed files with 168 additions and 6 deletions.
Empty file added demo/core/__init__.py
Empty file.
6 changes: 6 additions & 0 deletions demo/core/apps.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
from django.apps import AppConfig


class CoreConfig(AppConfig):
default_auto_field = "django.db.models.BigAutoField"
name = "core"
29 changes: 29 additions & 0 deletions demo/core/utils.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
from django.core.paginator import InvalidPage
from django.core.paginator import Paginator
from django.db.models import QuerySet
from django.http import Http404
from django.http import HttpRequest
from django.utils.translation import gettext_lazy as _


def paginate_queryset(request: HttpRequest, queryset: QuerySet, page_size: int = 20):
"""
Paginates a queryset, and returns a page object.
copied from https://github.com/carltongibson/neapolitan/blob/main/src/neapolitan/views.py
"""
paginator = Paginator(queryset, page_size)
page_number = request.GET.get("page") or 1
try:
page_number = int(page_number)
except ValueError:
if page_number == "last":
page_number = paginator.num_pages
else:
msg = "Page is not 'last', nor can it be converted to an int."
raise Http404(_(msg))

try:
return paginator.page(page_number)
except InvalidPage as exc:
msg = "Invalid page (%s): %s"
raise Http404(_(msg) % (page_number, str(exc)))
1 change: 1 addition & 0 deletions demo/demo/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
"django.contrib.sessions",
"django.contrib.messages",
"django.contrib.staticfiles",
"core",
"products",
]

Expand Down
3 changes: 2 additions & 1 deletion demo/demo/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,11 @@
from django.contrib import admin
from django.urls import include
from django.urls import path
from django.urls import reverse_lazy
from django.views.generic.base import RedirectView

urlpatterns = [
path("admin/", admin.site.urls),
path("", RedirectView.as_view(url="/products/")),
path("", RedirectView.as_view(url=reverse_lazy("products:product_list"))),
path("products/", include("products.urls")),
]
9 changes: 9 additions & 0 deletions demo/products/forms.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
from django.forms import ModelForm

from .models import Product


class ProductForm(ModelForm):
class Meta:
model = Product
fields = ("id", "name", "description", "price", "slug", "sku")
13 changes: 13 additions & 0 deletions demo/products/urls.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
from django.urls import path

from . import views

app_name = "products"

urlpatterns = [
path("products/", views.product_list, name="product_list"),
path("products/create/", views.product_create, name="product_create"),
path("products/<int:pk>/", views.product_detail, name="product_detail"),
path("products/<int:pk>/update/", views.product_update, name="product_update"),
path("products/<int:pk>/delete/", views.product_delete, name="product_delete"),
]
59 changes: 59 additions & 0 deletions demo/products/views.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
from core.utils import paginate_queryset
from django.http import HttpRequest
from django.http import HttpResponse
from django.shortcuts import get_object_or_404
from django.shortcuts import redirect
from django.template.response import TemplateResponse
from django.views.decorators.http import require_http_methods

from .forms import ProductForm
from .models import Product


def product_list(request: HttpRequest):
products = Product.objects.all()
return TemplateResponse(
request,
"products/product_list.html",
context={"products": paginate_queryset(request, products)},
)


def product_detail(request: HttpRequest, pk: int):
product = get_object_or_404(Product.objects, pk=pk)
return TemplateResponse(
request,
"products/product_detail.html",
context={"product": product},
)


def product_create(request: HttpRequest):
form = ProductForm(request.POST or None)
if request.method == "POST" and form.is_valid():
form.save()
return redirect("products:product_list")
return TemplateResponse(
request,
"products/product_create.html",
context={"form": form},
)


def product_update(request: HttpRequest, pk: int):
product = get_object_or_404(Product.objects, pk=pk)
form = ProductForm(request.POST or None, instance=product)
if request.method == "POST" and form.is_valid():
form.save()
return redirect("products:product_detail", pk=pk)
return TemplateResponse(
request,
"products/product_update.html",
context={"product": product, "form": form},
)


@require_http_methods(["DELETE"])
def product_delete(request: HttpRequest, pk: int):
Product.objects.filter(pk=pk).delete()
return HttpResponse("OK")
15 changes: 15 additions & 0 deletions demo/templates/base.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>My Website</title>
<script src="https://cdn.tailwindcss.com"></script>
</head>
<body>
<main class="container mx-auto prose">
{% block content %}
{% endblock %}
</main>
</body>
</html>
30 changes: 30 additions & 0 deletions demo/templates/products/product_list.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
{% extends 'base.html' %}

{% block content %}
<table>
<thead>
<tr>
<th>Product Name</th>
<th>Price</th>
</tr>
</thead>
<tbody>
{% for product in products %}
<tr>
<td>{{ product.name }}</td>
<td>{{ product.price }}</td>
</tr>
{% endfor %}
</tbody>
</table>

<div class="pagination">
{% if products.has_previous %}
<a href="?page={{ products.previous_page_number }}">Previous</a>
{% endif %}
<span class="current-page">{{ products.number }}</span>
{% if products.has_next %}
<a href="?page={{ products.next_page_number }}">Next</a>
{% endif %}
</div>
{% endblock %}
Empty file.
5 changes: 3 additions & 2 deletions docs/the_cli/crud.rst
Original file line number Diff line number Diff line change
Expand Up @@ -3,18 +3,19 @@ CRUD for your model

.. figure:: ../images/crud.svg


This command generates htmx-powered create, read, update, and delete views for your model. It follows a similar idea as `neapolitan <https://github.com/carltongibson/neapolitan>`_
but with a completely different approach. To use **neapolitan**, you'll inherit from its base class view, and for customization, get familiar with its API (which is fairly easy).
I prefer function-based views, so this command generates basic and simple function-based views with some basic HTML templates.

.. admonition:: Why function based views?
:class: hint dropdown

Read this `django views the right way <https://spookylukey.github.io/django-views-the-right-way/>`_ article.
I think class-based views get complex faster than function-based views. Both have their use cases, but function-based views
stay simpler to manage longer in my experience. There is an excellent document on the topic, read this `django views the right way <https://spookylukey.github.io/django-views-the-right-way/>`_.

This command depends on your ``manage.py`` to work, so you must run it from your project root directory.


**Examples**

.. code:: bash
Expand Down
4 changes: 1 addition & 3 deletions src/falco/commands/work.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,9 +38,7 @@ def __call__(self) -> None:

with suppress(FileNotFoundError):
pyproject_config = read_toml(Path("pyproject.toml"))
user_commands = (
pyproject_config.get("tool", {}).get("falco", {}).get("work", {})
)
user_commands = pyproject_config.get("tool", {}).get("falco", {}).get("work", {})
commands = commands | user_commands

manager = HonchoManager()
Expand Down

0 comments on commit 7eb12f0

Please sign in to comment.