feat: mixin for update forms
This commit is contained in:
+1
-50
@@ -1,9 +1,5 @@
|
||||
from django.conf import settings
|
||||
from django.core.paginator import Paginator
|
||||
from django import forms
|
||||
from django.db import models
|
||||
from django.views.generic import TemplateView
|
||||
from django.shortcuts import get_object_or_404, render
|
||||
|
||||
|
||||
class FilteredQuerysetMixin:
|
||||
@@ -62,11 +58,7 @@ class StylingMixin:
|
||||
for field_name in self.styled_fields:
|
||||
field = self.fields[field_name]
|
||||
field.required = True
|
||||
field.widget.attrs.update(
|
||||
{
|
||||
"class": ' '.join(self.classes)
|
||||
}
|
||||
)
|
||||
field.widget.attrs.update({"class": " ".join(self.classes)})
|
||||
|
||||
if self.placeholder_for_field.get(field_name):
|
||||
field.widget.attrs.update(
|
||||
@@ -74,44 +66,3 @@ class StylingMixin:
|
||||
"placeholder": self.placeholder_for_field.get(field_name),
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
class ReadUpdateView(TemplateView):
|
||||
model: models.Model = None
|
||||
form_class: forms.ModelForm = None
|
||||
template_name: str = ""
|
||||
ok_template_name: str = ""
|
||||
error_template_name: str = ""
|
||||
|
||||
def get_model(self):
|
||||
return self.model
|
||||
|
||||
def get_object(self):
|
||||
pk = self.kwargs.get('pk')
|
||||
cls = self.get_model()
|
||||
return get_object_or_404(cls, pk=pk)
|
||||
|
||||
def get_initial_values(self, instance):
|
||||
return {}
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
instance = self.get_object()
|
||||
form = self.form_class(initial=self.get_initial_values(instance))
|
||||
|
||||
return {
|
||||
"form": form,
|
||||
}
|
||||
|
||||
def post(self, request, *args, **kwargs):
|
||||
form = self.form_class(request.POST)
|
||||
|
||||
if form.is_valid():
|
||||
instance = form.save()
|
||||
return render(request, self.ok_template_name, {
|
||||
"form": form,
|
||||
"instance": instance,
|
||||
})
|
||||
|
||||
return render(request, self.error_template_name, {
|
||||
"form": form,
|
||||
})
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
{% load i18n %}
|
||||
{% load static %}
|
||||
<form hx-post="{{ url }}" hx-swap="none">
|
||||
{% csrf_token %}
|
||||
{% for field in form %}
|
||||
<div class="mb-4">
|
||||
<label for="{{ field.id_for_label }}"
|
||||
class="inline-block text-sm font-medium text-gray-900 dark:text-white ml-2">
|
||||
{{ field.label }}
|
||||
</label>
|
||||
{{ field }}
|
||||
</div>
|
||||
{% endfor %}
|
||||
|
||||
{% for k, error in form.errors.items %}
|
||||
<span class="flex justify-center mb-2 text-sm font-medium text-red-900 dark:text-red-400">{{ error }}</span>
|
||||
{% endfor %}
|
||||
|
||||
<button class="text-white bg-primary-600 hover:bg-primary-700 focus:ring-4 focus:outline-none
|
||||
focus:ring-primary-300 font-medium rounded-lg text-sm px-5 py-2.5 text-center dark:bg-primary-600
|
||||
dark:hover:bg-primary-700 dark:focus:ring-primary-800"
|
||||
>
|
||||
{% translate 'Guardar' %}
|
||||
</button>
|
||||
</form>
|
||||
@@ -1,28 +1,25 @@
|
||||
{% load i18n %}
|
||||
{% load static %}
|
||||
<form hx-post="{% url 'web:user_info_component' %}" hx-swap="none">
|
||||
{% csrf_token %}
|
||||
{% for field in form %}
|
||||
<form hx-post="{{ url }}" hx-swap="none">
|
||||
{% csrf_token %}
|
||||
{% for field in form %}
|
||||
<div class="mb-4">
|
||||
|
||||
<label for="{{ field.id_for_label }}"
|
||||
class="inline-block text-sm font-medium text-gray-900 dark:text-white ml-2">
|
||||
{{ field.label }}
|
||||
</label>
|
||||
{{ field }}
|
||||
<label for="{{ field.id_for_label }}"
|
||||
class="inline-block text-sm font-medium text-gray-900 dark:text-white ml-2">
|
||||
{{ field.label }}
|
||||
</label>
|
||||
{{ field }}
|
||||
</div>
|
||||
{% endfor %}
|
||||
{% endfor %}
|
||||
|
||||
{% for k, error in form.errors.items %}
|
||||
{% for k, error in form.errors.items %}
|
||||
<span class="flex justify-center mb-2 text-sm font-medium text-red-900 dark:text-red-400">{{ error }}</span>
|
||||
{% endfor %}
|
||||
{% endfor %}
|
||||
|
||||
<button class="text-white bg-primary-600 hover:bg-primary-700 focus:ring-4 focus:outline-none
|
||||
<button class="text-white bg-primary-600 hover:bg-primary-700 focus:ring-4 focus:outline-none
|
||||
focus:ring-primary-300 font-medium rounded-lg text-sm px-5 py-2.5 text-center dark:bg-primary-600
|
||||
dark:hover:bg-primary-700 dark:focus:ring-primary-800"
|
||||
>
|
||||
{% translate 'Guardar' %}
|
||||
</button>
|
||||
|
||||
|
||||
>
|
||||
{% translate 'Guardar' %}
|
||||
</button>
|
||||
</form>
|
||||
|
||||
@@ -2,11 +2,11 @@
|
||||
{% load static %}
|
||||
{% for address in customer_addresses %}
|
||||
<form
|
||||
id="edit_address_{{ address.pk }}"
|
||||
class="hidden py-2 md:py-4"
|
||||
hx-post="{% url 'web:update_customer_address' pk=address.pk %}"
|
||||
hx-on:htmx:after-request="htmx.toggleClass(htmx.find('#edit_address_{{ address.pk }}'), 'hidden'); htmx.toggleClass(htmx.find('#details_address_{{ address.pk }}'), 'hidden')"
|
||||
hx-swap="none"
|
||||
id="edit_address_{{ address.pk }}"
|
||||
class="hidden py-2 md:py-4"
|
||||
hx-post="{% url 'web:update_customer_address' pk=address.pk %}"
|
||||
hx-on:htmx:after-request="htmx.toggleClass(htmx.find('#edit_address_{{ address.pk }}'), 'hidden'); htmx.toggleClass(htmx.find('#details_address_{{ address.pk }}'), 'hidden')"
|
||||
hx-swap="none"
|
||||
>
|
||||
{% csrf_token %}
|
||||
<div class="mb-4 grid gap-4 sm:grid-cols-2 sm:gap-8 lg:gap-16">
|
||||
@@ -159,14 +159,8 @@
|
||||
{% translate 'Editar datos' %}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
{% if not forloop.last %}
|
||||
<hr class="h-px my-2 bg-gray-200 border-0 dark:bg-gray-700">
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -10,8 +10,15 @@
|
||||
{% translate 'Mi cuenta' %}
|
||||
</h2>
|
||||
<div
|
||||
class="mt-4 p-6 border border-gray-100 rounded-md dark:border-gray-600"
|
||||
hx-get="{% url 'web:user_info_component' %}" hx-trigger="load"
|
||||
class="mt-4 p-6 border border-gray-100 rounded-md dark:border-gray-600"
|
||||
hx-get="{% url 'web:user_info_component' %}" hx-trigger="load, updated_user_info"
|
||||
>
|
||||
|
||||
</div>
|
||||
|
||||
<div
|
||||
class="mt-4 p-6 border border-gray-100 rounded-md dark:border-gray-600"
|
||||
hx-get="{% url 'web:change_password_component' %}" hx-trigger="load, changed_password"
|
||||
>
|
||||
|
||||
</div>
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
from django.contrib.auth.models import User
|
||||
from django.test import TestCase
|
||||
from django.urls import reverse
|
||||
|
||||
from shop.tests.mixins import CreateProductsMixin
|
||||
|
||||
|
||||
class TestChangeUserPassword(TestCase, CreateProductsMixin):
|
||||
def setUp(self):
|
||||
self.password = "ihatesand"
|
||||
self.user = User.objects.create_user("vader", "darth@vader.com", self.password)
|
||||
|
||||
def test_update_user_info(self):
|
||||
self.client.force_login(self.user)
|
||||
response = self.client.get(reverse("web:change_password_component"))
|
||||
|
||||
assert response.status_code == 200
|
||||
new_password = "ifnhatesandbro"
|
||||
data = {
|
||||
"old_password": self.password,
|
||||
"new_password1": new_password,
|
||||
"new_password2": new_password,
|
||||
}
|
||||
|
||||
response = self.client.post(reverse("web:change_password_component"), data)
|
||||
|
||||
assert response.status_code == 200
|
||||
self.user.refresh_from_db()
|
||||
|
||||
assert self.user.check_password(new_password)
|
||||
@@ -11,7 +11,7 @@ class TestCustomerAddresses(TestCase, CreateProductsMixin):
|
||||
self.user = User.objects.create_user("vader", "darth@vader.com", "ihatesand")
|
||||
self.customer_address = CustomerAddress.objects.create(
|
||||
user=self.user,
|
||||
full_name='Darth Vader',
|
||||
full_name="Darth Vader",
|
||||
address_type=CustomerAddress.Types.SHIPPING,
|
||||
address="Vader Fortress",
|
||||
address_town="Mustafar",
|
||||
@@ -33,13 +33,18 @@ class TestCustomerAddresses(TestCase, CreateProductsMixin):
|
||||
"address_zip": "00001",
|
||||
}
|
||||
|
||||
response = self.client.post(reverse("web:update_customer_address", kwargs={"pk": self.customer_address.pk}), data)
|
||||
response = self.client.post(
|
||||
reverse(
|
||||
"web:update_customer_address", kwargs={"pk": self.customer_address.pk}
|
||||
),
|
||||
data,
|
||||
)
|
||||
|
||||
assert response.status_code == 200
|
||||
self.customer_address.refresh_from_db()
|
||||
|
||||
assert self.customer_address.full_name == data.get('full_name')
|
||||
assert self.customer_address.address == data.get('address')
|
||||
assert self.customer_address.address_town == data.get('address_town')
|
||||
assert self.customer_address.address_state == data.get('address_state')
|
||||
assert self.customer_address.address_phone == data.get('address_phone')
|
||||
assert self.customer_address.full_name == data.get("full_name")
|
||||
assert self.customer_address.address == data.get("address")
|
||||
assert self.customer_address.address_town == data.get("address_town")
|
||||
assert self.customer_address.address_state == data.get("address_state")
|
||||
assert self.customer_address.address_phone == data.get("address_phone")
|
||||
|
||||
@@ -2,7 +2,6 @@ from django.contrib.auth.models import User
|
||||
from django.test import TestCase
|
||||
from django.urls import reverse
|
||||
|
||||
from shop.models import CustomerAddress
|
||||
from shop.tests.mixins import CreateProductsMixin
|
||||
|
||||
|
||||
@@ -13,12 +12,13 @@ class TestUserInfo(TestCase, CreateProductsMixin):
|
||||
def test_update_user_info(self):
|
||||
self.client.force_login(self.user)
|
||||
response = self.client.get(reverse("web:user_info_component"))
|
||||
|
||||
assert response.status_code == 200
|
||||
|
||||
data = {
|
||||
"first_name": "Anakin",
|
||||
"last_name": "Skywalker",
|
||||
"email": "anakin@skywalker.com"
|
||||
"email": "anakin@skywalker.com",
|
||||
}
|
||||
|
||||
response = self.client.post(reverse("web:user_info_component"), data)
|
||||
@@ -26,6 +26,6 @@ class TestUserInfo(TestCase, CreateProductsMixin):
|
||||
assert response.status_code == 200
|
||||
self.user.refresh_from_db()
|
||||
|
||||
assert self.user.first_name == data.get('first_name')
|
||||
assert self.user.last_name == data.get('last_name')
|
||||
assert self.user.email == data.get('email')
|
||||
assert self.user.first_name == data.get("first_name")
|
||||
assert self.user.last_name == data.get("last_name")
|
||||
assert self.user.email == data.get("email")
|
||||
|
||||
+6
-1
@@ -11,7 +11,7 @@ from web.views.components import (
|
||||
list_wishlisted_products,
|
||||
update_customer_address,
|
||||
user_info_component,
|
||||
wishlist_button,
|
||||
wishlist_button, change_password_component,
|
||||
)
|
||||
from web.views.users import (
|
||||
login,
|
||||
@@ -91,6 +91,11 @@ urlpatterns = [
|
||||
login_required(user_info_component, login_url=_("/login/")),
|
||||
name="user_info_component",
|
||||
),
|
||||
path(
|
||||
"web/components/change-password/",
|
||||
login_required(change_password_component, login_url=_("/login/")),
|
||||
name="change_password_component",
|
||||
),
|
||||
# api
|
||||
path("web/add-cart-item/", add_cart_item, name="add_cart_item"),
|
||||
path("web/delete-cart-item/<int:pk>/", delete_cart_item, name="delete_cart_item"),
|
||||
|
||||
+31
-25
@@ -1,9 +1,12 @@
|
||||
from decimal import Decimal
|
||||
|
||||
from django.db import models
|
||||
from django.http import HttpResponse
|
||||
from django.shortcuts import get_object_or_404, render
|
||||
from django.urls import reverse
|
||||
from django.views.generic import TemplateView
|
||||
|
||||
from config.mixins import HTMXFormComponent
|
||||
from shop.filters import ProductFilter
|
||||
from shop.models import (
|
||||
CartItem,
|
||||
@@ -13,10 +16,10 @@ from shop.models import (
|
||||
ShippingMethod,
|
||||
WishlistedProduct,
|
||||
)
|
||||
from users.forms.change_password import ChangePasswordForm
|
||||
from users.forms.info import UserInfoForm
|
||||
from web.forms import CustomerAddressForm
|
||||
from web.mixins import FilteredQuerysetMixin, PaginatedQuerysetMixin
|
||||
from web.models import WebSettings
|
||||
from web.settings import ANONYMOUS_CART_ID_COOKIE_NAME
|
||||
from web.utils import get_or_create_cart
|
||||
|
||||
@@ -146,37 +149,39 @@ class WishlistButton(TemplateView):
|
||||
}
|
||||
|
||||
|
||||
class UserInfoComponentView(TemplateView):
|
||||
template_name = "components/users/info.html"
|
||||
class UserInfoFormComponentView(HTMXFormComponent):
|
||||
form_class = UserInfoForm
|
||||
hx_trigger = "updated_user_info"
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
web_settings = WebSettings.load()
|
||||
user = self.request.user
|
||||
form = self.form_class(
|
||||
initial={
|
||||
"email": user.email,
|
||||
"first_name": user.first_name,
|
||||
"last_name": user.last_name,
|
||||
}
|
||||
)
|
||||
def get_object(self) -> models.Model:
|
||||
return self.request.user
|
||||
|
||||
def get_view_name(self):
|
||||
return reverse("web:user_info_component")
|
||||
|
||||
def get_initial_values(self, instance):
|
||||
return {
|
||||
"form": form,
|
||||
"email": instance.email,
|
||||
"first_name": instance.first_name,
|
||||
"last_name": instance.last_name,
|
||||
}
|
||||
|
||||
def post(self, request, *args, **kwargs):
|
||||
user = self.request.user
|
||||
form = self.form_class(request.POST)
|
||||
|
||||
if form.is_valid():
|
||||
user.email = form.cleaned_data.get("email")
|
||||
user.first_name = form.cleaned_data.get("first_name")
|
||||
user.last_name = form.cleaned_data.get("last_name")
|
||||
user.save()
|
||||
return HttpResponse(status=200)
|
||||
class ChangePasswordFormComponentView(HTMXFormComponent):
|
||||
form_class = ChangePasswordForm
|
||||
hx_trigger = "changed_password"
|
||||
|
||||
return render(request, self.template_name, {"form": form})
|
||||
def get_form(self, initial=True):
|
||||
if initial:
|
||||
return self.form_class(user=self.request.user)
|
||||
|
||||
return self.form_class(self.request.user, self.request.POST)
|
||||
|
||||
def get_object(self) -> models.Model:
|
||||
return self.request.user
|
||||
|
||||
def get_view_name(self):
|
||||
return reverse("web:change_password_component")
|
||||
|
||||
|
||||
class ListCustomerAddressComponent(TemplateView):
|
||||
@@ -203,5 +208,6 @@ list_products = ListProducts.as_view()
|
||||
list_wishlisted_products = ListWishlistedProducts.as_view()
|
||||
wishlist_button = WishlistButton.as_view()
|
||||
|
||||
user_info_component = UserInfoComponentView.as_view()
|
||||
user_info_component = UserInfoFormComponentView.as_view()
|
||||
change_password_component = ChangePasswordFormComponentView.as_view()
|
||||
list_customer_addresses_component = ListCustomerAddressComponent.as_view()
|
||||
|
||||
Reference in New Issue
Block a user