Coverage for src/products/mixins.py: 100%

27 statements  

« prev     ^ index     » next       coverage.py v7.9.2, created at 2025-08-04 12:59 +0300

1from django.db import models 

2from django.db.models import Q 

3from src.products.constants import NameFieldLengths 

4 

5 

6class NameFieldMixin(models.Model): 

7 """ 

8 Mixin class that provides a standardized name field for various entities. 

9 

10 This mixin is used by entities like Collection, Color, Metal, Stone, and Size 

11 to ensure they all have a consistent name field with the same constraints. 

12 Using a mixin instead of repeating the field definition in each model 

13 promotes DRY (Don't Repeat Yourself) principles and makes maintenance easier. 

14 

15 The mixin uses the NAME_MAX_LENGTH constant from the constants module, 

16 ensuring consistency across the application. 

17 """ 

18 

19 NAME_MAX_LENGTH = NameFieldLengths.NAME_MAX_LENGTH 

20 

21 class Meta: 

22 abstract = True 

23 

24 # Standardized name field used across multiple entities 

25 name = models.CharField( 

26 max_length=NAME_MAX_LENGTH, 

27 unique=True, 

28 ) 

29 

30 def __str__(self): 

31 return self.name 

32 

33 

34class FilterMixin: 

35 """ 

36 Mixin class that provides filtering functionality for product listings. 

37 

38 This mixin is used by views that need to filter products based on 

39 various attributes like colors, stones, metals, and collections. 

40 It provides methods to extract filter parameters from request query 

41 strings and build database queries. 

42 

43 The mixin supports both filtering by product attributes directly 

44 and filtering by related inventory attributes. 

45 """ 

46 

47 def _get_params(self): 

48 """ 

49 Extract filter parameters from the request query string. 

50 

51 This method looks for specific query parameters in the request 

52 and returns them as a dictionary. The parameters are expected 

53 to be lists (multiple values can be selected for each filter). 

54 """ 

55 return { 

56 'colors': self.request.query_params.getlist('colors'), 

57 'stones': self.request.query_params.getlist('stones'), 

58 'metals': self.request.query_params.getlist('metals'), 

59 'collections': self.request.query_params.getlist('collections'), 

60 } 

61 

62 def _build_filters(self, params, filter_map): 

63 """ 

64 Build database filters based on provided parameters and filter mapping. 

65 """ 

66 filters = Q() 

67 

68 for key, value in params.items(): 

69 if value and key in filter_map: 

70 filters &= Q(**{filter_map[key]: value}) 

71 

72 return filters 

73 

74 def _get_filters_for_attributes(self, category): 

75 """ 

76 Build database filters for attribute-based filtering. 

77 """ 

78 params = self._get_params() 

79 

80 filter_map = { 

81 'colors': f'{category}__color_id__in', 

82 'stones': f'{category}__stone_id__in', 

83 'metals': f'{category}__metal_id__in', 

84 'collections': f'{category}__collection_id__in', 

85 } 

86 

87 return self._build_filters(params, filter_map) 

88 

89 def _get_filters_for_product(self): 

90 """ 

91 Build database filters for direct product filtering. 

92 """ 

93 params = self._get_params() 

94 

95 filter_map = { 

96 'colors': 'color_id__in', 

97 'stones': 'stone_id__in', 

98 'metals': 'metal__id__in', 

99 'collections': 'collection__id__in', 

100 } 

101 

102 return self._build_filters(params, filter_map)