from django.views.generic import CreateView, ListView, UpdateView, DeleteView
from django.urls import reverse_lazy
from django.http import HttpResponse, HttpResponseBadRequest
from django.shortcuts import get_object_or_404
from .models import Product
from .forms import ProductForm
class ProductUpdateView(UpdateView):
model = Product
form_class = ProductForm
template_name = "products/product_form.html"
success_url = reverse_lazy("product_list")
def get_template_names(self):
if self.request.htmx:
return ["products/_product_form.html"]
return super().get_template_names()
def form_valid(self, form):
response = super().form_valid(form)
if self.request.htmx:
return render(self.request, "products/_product_list.html", {"products": Product.objects.all()})
return response
urls.py
from django.urls import path
from .views import (
ProductUpdateView,
)
urlpatterns = [
path("edit/<int:pk>/", ProductUpdateView.as_view(), name="product_edit"),
]list.html
<tbody class="bg-white divide-y divide-gray-200">
{% for product in products %}
<tr class="hover:bg-gray-50 transition-colors duration-150" id="product-{{ product.id }}">
<td class="px-6 py-4 whitespace-nowrap">{{ product.name }}</td>
<td class="px-6 py-4 whitespace-nowrap">${{ product.price }}</td>
<td class="px-6 py-4">{{ product.description|truncatechars:50 }}</td>
<td class="px-6 py-4 whitespace-nowrap text-right text-sm font-medium">
<!-- Edit Button -->
<button hx-get="{% url 'product_edit' product.pk %}"
hx-target="#main-content"
hx-push-url="true"
class="text-blue-600 hover:text-blue-900 mr-4">
Edit
</button>
</td>
</tr>
{% endfor %}
</tbody>form.html
<!-- templates/products/_product_form.html -->
<div class="max-w-2xl mx-auto">
<!-- Form Header -->
<div class="mb-8 border-b border-gray-200 pb-4">
<h2 class="text-3xl font-bold text-gray-900">
{% if object %}Edit{% else %}Create New{% endif %} Product
</h2>
<p class="mt-1 text-sm text-gray-500">
{% if object %}Update the product details{% else %}Fill the form to add a new product{% endif %}
</p>
</div>
<form method="POST"
{% if object %}
hx-post="{% url 'product_edit' object.pk %}"
{% else %}
hx-post="{% url 'product_create' %}"
{% endif %}
hx-target="#main-content">
{% csrf_token %}
<div class="space-y-6">
<!-- Name Field -->
<div class="relative z-0">
{{ form.name }}
<label for="{{ form.name.id_for_label }}"
class="absolute left-3 -top-3.5 bg-white px-1 text-gray-600 text-sm transition-all
peer-placeholder-shown:text-base peer-placeholder-shown:text-gray-400
peer-placeholder-shown:top-2 peer-focus:-top-3.5 peer-focus:text-sm peer-focus:text-blue-600">
Product Name
</label>
</div>
<!-- Price Field -->
<div class="relative z-0">
<div class="relative">
<span class="absolute left-3 top-1/2 -translate-y-1/2 text-gray-400">$</span>
{{ form.price }}
<label for="{{ form.price.id_for_label }}"
class="absolute left-8 -top-3.5 bg-white px-1 text-gray-600 text-sm transition-all
peer-placeholder-shown:text-base peer-placeholder-shown:text-gray-400
peer-placeholder-shown:top-2 peer-focus:-top-3.5 peer-focus:text-sm peer-focus:text-blue-600">
Price
</label>
</div>
</div>
<!-- Description Field -->
<div class="relative z-0">
{{ form.description }}
<label for="{{ form.description.id_for_label }}"
class="absolute left-3 -top-3.5 bg-white px-1 text-gray-600 text-sm transition-all
peer-placeholder-shown:text-base peer-placeholder-shown:text-gray-400
peer-placeholder-shown:top-2 peer-focus:-top-3.5 peer-focus:text-sm peer-focus:text-blue-600">
Description
</label>
</div>
<!-- Form Actions -->
<div class="pt-6 border-t border-gray-200 flex justify-end space-x-4">
<a href="{% url 'product_list' %}"
hx-get="{% url 'product_list' %}"
hx-target="#main-content"
class="px-6 py-2.5 border border-gray-300 rounded-lg text-gray-700 hover:bg-gray-50 transition-colors">
Cancel
</a>
<button type="submit"
class="px-6 py-2.5 bg-gradient-to-r from-blue-600 to-purple-600 text-white
rounded-lg shadow-md hover:shadow-lg transition-all duration-200">
{% if object %}Update{% else %}Create{% endif %} Product
</button>
</div>
</div>
</form>
</div>