説明なし

forms.py 6.2KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187
  1. from __future__ import annotations
  2. from django import forms
  3. from django.contrib.auth import get_user_model
  4. from django.core.exceptions import ValidationError
  5. from crispy_forms.helper import FormHelper
  6. from crispy_forms.layout import Layout, Field
  7. from django.contrib.auth import get_user_model
  8. from orgs.models import UserProfile
  9. class MultiFileInput(forms.ClearableFileInput):
  10. allow_multiple_selected = True
  11. class MultiFileField(forms.Field):
  12. widget = MultiFileInput
  13. def __init__(self, *args, **kwargs):
  14. kwargs.setdefault("required", False)
  15. super().__init__(*args, **kwargs)
  16. def to_python(self, data):
  17. # Accept a list of UploadedFile or an empty list/None
  18. return data
  19. def validate(self, value):
  20. # Only enforce presence if required=True
  21. if self.required and not value:
  22. raise forms.ValidationError("This field is required.")
  23. class PickupRequestForm(forms.Form):
  24. name = forms.CharField(max_length=255)
  25. email = forms.EmailField(required=False)
  26. phone = forms.CharField(max_length=64, required=False)
  27. address = forms.CharField(widget=forms.Textarea)
  28. preferred_at = forms.DateTimeField(required=False, widget=forms.DateTimeInput(attrs={"type": "datetime-local"}))
  29. materials = forms.CharField(label="Materials/Notes", widget=forms.Textarea, required=False)
  30. photos = MultiFileField(widget=MultiFileInput, help_text="Optional: upload photos of scrap")
  31. class ContactForm(forms.Form):
  32. name = forms.CharField(max_length=255)
  33. email = forms.EmailField()
  34. phone = forms.CharField(max_length=64, required=False)
  35. subject = forms.CharField(max_length=255, required=False)
  36. message = forms.CharField(widget=forms.Textarea)
  37. class RegistrationForm(forms.Form):
  38. username = forms.CharField(max_length=150)
  39. email = forms.EmailField()
  40. password1 = forms.CharField(widget=forms.PasswordInput)
  41. password2 = forms.CharField(widget=forms.PasswordInput)
  42. role = forms.ChoiceField(choices=[(k, v) for k, v in UserProfile.ROLE_CHOICES if k != UserProfile.ROLE_OWNER])
  43. def clean_username(self):
  44. username = self.cleaned_data["username"].strip()
  45. User = get_user_model()
  46. if User.objects.filter(username__iexact=username).exists():
  47. raise forms.ValidationError("Username already taken.")
  48. return username
  49. def clean_email(self):
  50. email = self.cleaned_data["email"].strip()
  51. User = get_user_model()
  52. if User.objects.filter(email__iexact=email).exists():
  53. raise forms.ValidationError("Email already registered.")
  54. return email
  55. def clean(self):
  56. data = super().clean()
  57. p1 = data.get("password1")
  58. p2 = data.get("password2")
  59. if p1 and p2 and p1 != p2:
  60. self.add_error("password2", "Passwords do not match.")
  61. return data
  62. class PublicUserForm(forms.ModelForm):
  63. class Meta:
  64. model = get_user_model()
  65. fields = ["first_name", "last_name", "email"]
  66. def __init__(self, *args, **kwargs):
  67. super().__init__(*args, **kwargs)
  68. self.helper = FormHelper()
  69. self.helper.form_tag = False
  70. self.helper.layout = Layout(
  71. Field("first_name"),
  72. Field("last_name"),
  73. Field("email"),
  74. )
  75. from api.models import Profile as ApiProfile
  76. from orgs.models import UserProfile
  77. class PublicProfileForm(forms.ModelForm):
  78. tags = forms.CharField(label="Tags", required=False, help_text="Comma-separated")
  79. class Meta:
  80. model = ApiProfile
  81. fields = ["bio", "interests", "industry"]
  82. def __init__(self, *args, **kwargs):
  83. super().__init__(*args, **kwargs)
  84. self.helper = FormHelper()
  85. self.helper.form_tag = False
  86. self.helper.layout = Layout(
  87. Field("bio"),
  88. Field("interests"),
  89. Field("industry"),
  90. Field("tags"),
  91. )
  92. if self.instance and getattr(self.instance, "pk", None):
  93. try:
  94. names = list(self.instance.tags.names())
  95. self.fields["tags"].initial = ", ".join(names)
  96. except Exception:
  97. self.fields["tags"].initial = ""
  98. def save(self, commit: bool = True):
  99. profile = super().save(commit)
  100. tags = [t.strip() for t in self.cleaned_data.get("tags", "").split(",") if t.strip()]
  101. try:
  102. if commit and hasattr(profile, "tags"):
  103. profile.tags.set(tags)
  104. except Exception:
  105. pass
  106. return profile
  107. class PublicUserPhotoForm(forms.ModelForm):
  108. remove_photo = forms.BooleanField(label="Remove current photo", required=False)
  109. class Meta:
  110. model = UserProfile
  111. fields = ["my_photo", "remove_photo"]
  112. def __init__(self, *args, **kwargs):
  113. super().__init__(*args, **kwargs)
  114. self.fields["my_photo"].widget = forms.ClearableFileInput(attrs={
  115. "accept": "image/*",
  116. "class": "hidden",
  117. "id": "id_my_photo",
  118. })
  119. self.helper = FormHelper()
  120. self.helper.form_tag = False
  121. self.helper.layout = Layout(
  122. Field("my_photo"),
  123. Field("remove_photo"),
  124. )
  125. def clean_my_photo(self):
  126. f = self.cleaned_data.get("my_photo")
  127. if not f:
  128. return f
  129. try:
  130. content_type = getattr(f, "content_type", "")
  131. if content_type and not content_type.startswith("image/"):
  132. raise ValidationError("Please upload an image file.")
  133. except Exception:
  134. pass
  135. max_bytes = 5 * 1024 * 1024
  136. if getattr(f, "size", 0) and f.size > max_bytes:
  137. raise ValidationError("Image too large (max 5MB).")
  138. return f
  139. def save(self, commit: bool = True):
  140. instance: UserProfile = super().save(commit=False)
  141. remove = self.cleaned_data.get("remove_photo", False)
  142. new_file = self.cleaned_data.get("my_photo")
  143. if remove and not new_file:
  144. try:
  145. if instance.my_photo:
  146. instance.my_photo.delete(save=False)
  147. except Exception:
  148. pass
  149. instance.my_photo = None
  150. if commit:
  151. instance.save()
  152. return instance