| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187 |
- from __future__ import annotations
- from django import forms
- from django.contrib.auth import get_user_model
- from django.core.exceptions import ValidationError
- from crispy_forms.helper import FormHelper
- from crispy_forms.layout import Layout, Field
- from django.contrib.auth import get_user_model
- from orgs.models import UserProfile
- class MultiFileInput(forms.ClearableFileInput):
- allow_multiple_selected = True
- class MultiFileField(forms.Field):
- widget = MultiFileInput
- def __init__(self, *args, **kwargs):
- kwargs.setdefault("required", False)
- super().__init__(*args, **kwargs)
- def to_python(self, data):
- # Accept a list of UploadedFile or an empty list/None
- return data
- def validate(self, value):
- # Only enforce presence if required=True
- if self.required and not value:
- raise forms.ValidationError("This field is required.")
- class PickupRequestForm(forms.Form):
- name = forms.CharField(max_length=255)
- email = forms.EmailField(required=False)
- phone = forms.CharField(max_length=64, required=False)
- address = forms.CharField(widget=forms.Textarea)
- preferred_at = forms.DateTimeField(required=False, widget=forms.DateTimeInput(attrs={"type": "datetime-local"}))
- materials = forms.CharField(label="Materials/Notes", widget=forms.Textarea, required=False)
- photos = MultiFileField(widget=MultiFileInput, help_text="Optional: upload photos of scrap")
- class ContactForm(forms.Form):
- name = forms.CharField(max_length=255)
- email = forms.EmailField()
- phone = forms.CharField(max_length=64, required=False)
- subject = forms.CharField(max_length=255, required=False)
- message = forms.CharField(widget=forms.Textarea)
- class RegistrationForm(forms.Form):
- username = forms.CharField(max_length=150)
- email = forms.EmailField()
- password1 = forms.CharField(widget=forms.PasswordInput)
- password2 = forms.CharField(widget=forms.PasswordInput)
- role = forms.ChoiceField(choices=[(k, v) for k, v in UserProfile.ROLE_CHOICES if k != UserProfile.ROLE_OWNER])
- def clean_username(self):
- username = self.cleaned_data["username"].strip()
- User = get_user_model()
- if User.objects.filter(username__iexact=username).exists():
- raise forms.ValidationError("Username already taken.")
- return username
- def clean_email(self):
- email = self.cleaned_data["email"].strip()
- User = get_user_model()
- if User.objects.filter(email__iexact=email).exists():
- raise forms.ValidationError("Email already registered.")
- return email
- def clean(self):
- data = super().clean()
- p1 = data.get("password1")
- p2 = data.get("password2")
- if p1 and p2 and p1 != p2:
- self.add_error("password2", "Passwords do not match.")
- return data
- class PublicUserForm(forms.ModelForm):
- class Meta:
- model = get_user_model()
- fields = ["first_name", "last_name", "email"]
- def __init__(self, *args, **kwargs):
- super().__init__(*args, **kwargs)
- self.helper = FormHelper()
- self.helper.form_tag = False
- self.helper.layout = Layout(
- Field("first_name"),
- Field("last_name"),
- Field("email"),
- )
- from api.models import Profile as ApiProfile
- from orgs.models import UserProfile
- class PublicProfileForm(forms.ModelForm):
- tags = forms.CharField(label="Tags", required=False, help_text="Comma-separated")
- class Meta:
- model = ApiProfile
- fields = ["bio", "interests", "industry"]
- def __init__(self, *args, **kwargs):
- super().__init__(*args, **kwargs)
- self.helper = FormHelper()
- self.helper.form_tag = False
- self.helper.layout = Layout(
- Field("bio"),
- Field("interests"),
- Field("industry"),
- Field("tags"),
- )
- if self.instance and getattr(self.instance, "pk", None):
- try:
- names = list(self.instance.tags.names())
- self.fields["tags"].initial = ", ".join(names)
- except Exception:
- self.fields["tags"].initial = ""
- def save(self, commit: bool = True):
- profile = super().save(commit)
- tags = [t.strip() for t in self.cleaned_data.get("tags", "").split(",") if t.strip()]
- try:
- if commit and hasattr(profile, "tags"):
- profile.tags.set(tags)
- except Exception:
- pass
- return profile
- class PublicUserPhotoForm(forms.ModelForm):
- remove_photo = forms.BooleanField(label="Remove current photo", required=False)
- class Meta:
- model = UserProfile
- fields = ["my_photo", "remove_photo"]
- def __init__(self, *args, **kwargs):
- super().__init__(*args, **kwargs)
- self.fields["my_photo"].widget = forms.ClearableFileInput(attrs={
- "accept": "image/*",
- "class": "hidden",
- "id": "id_my_photo",
- })
- self.helper = FormHelper()
- self.helper.form_tag = False
- self.helper.layout = Layout(
- Field("my_photo"),
- Field("remove_photo"),
- )
- def clean_my_photo(self):
- f = self.cleaned_data.get("my_photo")
- if not f:
- return f
- try:
- content_type = getattr(f, "content_type", "")
- if content_type and not content_type.startswith("image/"):
- raise ValidationError("Please upload an image file.")
- except Exception:
- pass
- max_bytes = 5 * 1024 * 1024
- if getattr(f, "size", 0) and f.size > max_bytes:
- raise ValidationError("Image too large (max 5MB).")
- return f
- def save(self, commit: bool = True):
- instance: UserProfile = super().save(commit=False)
- remove = self.cleaned_data.get("remove_photo", False)
- new_file = self.cleaned_data.get("my_photo")
- if remove and not new_file:
- try:
- if instance.my_photo:
- instance.my_photo.delete(save=False)
- except Exception:
- pass
- instance.my_photo = None
- if commit:
- instance.save()
- return instance
|