Coverage for website/forms/PostForm.py: 81%

36 statements  

« prev     ^ index     » next       coverage.py v7.5.0, created at 2025-09-13 15:29 -0300

1from django import forms 

2 

3from website.models.PostModel import Post 

4from website.utils.sanitizer import sanitize_html 

5 

6 

7class PostForm(forms.ModelForm): 

8 """Enhanced form for creating and editing blog posts with rich text editor""" 

9 

10 class Meta: 

11 model = Post 

12 fields = ["title", "url_slug", "meta_description", "text"] 

13 widgets = { 

14 "title": forms.TextInput( 

15 attrs={ 

16 "class": "form-control", 

17 "placeholder": "Enter an engaging post title", 

18 "maxlength": "200", 

19 } 

20 ), 

21 "url_slug": forms.TextInput( 

22 attrs={ 

23 "class": "form-control", 

24 "placeholder": "my-awesome-post", 

25 "pattern": "[a-z0-9-]+", 

26 "title": "Only lowercase letters, numbers, and hyphens allowed", 

27 } 

28 ), 

29 "meta_description": forms.Textarea( 

30 attrs={ 

31 "class": "form-control", 

32 "placeholder": "Write a compelling meta description for SEO (max 160 characters)", 

33 "rows": 3, 

34 "maxlength": "160", 

35 } 

36 ), 

37 "text": forms.Textarea( 

38 attrs={ 

39 "class": "form-control rich-text-editor", 

40 "id": "rich-editor", 

41 "placeholder": "Start writing your amazing content here...", 

42 } 

43 ), 

44 } 

45 labels = { 

46 "title": "Post Title", 

47 "url_slug": "URL Slug", 

48 "meta_description": "Meta Description (SEO)", 

49 "text": "Content", 

50 } 

51 help_texts = { 

52 "url_slug": "A unique URL-friendly identifier (use lowercase letters, numbers, and hyphens only)", 

53 "meta_description": "This appears in search engine results. Keep it under 160 characters.", 

54 "text": "Use the rich text editor to format your content with headings, images, videos, and tables.", 

55 } 

56 

57 def clean_url_slug(self): 

58 """Validate that the URL slug is unique and properly formatted""" 

59 url_slug = self.cleaned_data.get("url_slug") 

60 

61 # Convert to lowercase and replace spaces with hyphens 

62 url_slug = url_slug.lower().replace(" ", "-") 

63 

64 # Remove any characters that aren't letters, numbers, or hyphens 

65 import re 

66 

67 url_slug = re.sub(r"[^a-z0-9-]", "", url_slug) 

68 

69 # Remove multiple consecutive hyphens 

70 url_slug = re.sub(r"-+", "-", url_slug) 

71 

72 # Remove leading/trailing hyphens 

73 url_slug = url_slug.strip("-") 

74 

75 if not url_slug: 

76 raise forms.ValidationError("URL slug cannot be empty after formatting.") 

77 

78 # Check if the slug already exists (excluding the current instance if editing) 

79 if self.instance.pk: 

80 if ( 

81 Post.objects.filter(url_slug=url_slug) 

82 .exclude(pk=self.instance.pk) 

83 .exists() 

84 ): 

85 raise forms.ValidationError( 

86 "This URL slug is already in use. Please choose a different one." 

87 ) 

88 else: 

89 if Post.objects.filter(url_slug=url_slug).exists(): 

90 raise forms.ValidationError( 

91 "This URL slug is already in use. Please choose a different one." 

92 ) 

93 

94 return url_slug 

95 

96 def clean_meta_description(self): 

97 """Validate meta description length""" 

98 meta_description = self.cleaned_data.get("meta_description") 

99 if meta_description and len(meta_description) > 160: 

100 raise forms.ValidationError( 

101 "Meta description must be 160 characters or less." 

102 ) 

103 return meta_description 

104 

105 def clean_text(self): 

106 text = self.cleaned_data.get("text") 

107 # Sanitize rich text before saving 

108 try: 

109 return sanitize_html(text) 

110 except Exception: 

111 return text