Coverage for catalog/models.py: 94.29%

68 statements  

« prev     ^ index     » next       coverage.py v7.17.0, created at 2026-10-29 10:34 -0700

1import uuid 1a

2from datetime import date 1a

3from typing import ClassVar 1a

4 

5from django.contrib.auth import get_user_model 1a

6from django.db import models 1a

7from django.urls import reverse 1a

8 

9 

10class Genre(models.Model): 1a

11 name = models.CharField(max_length=200, help_text="Enter a book genre") 1a

12 

13 class Meta: 1a

14 ordering: ClassVar[list[str]] = ["name"] 1a

15 

16 def __str__(self): 1a

17 return self.name 1a

18 

19 

20class Language(models.Model): 1a

21 name = models.CharField(max_length=200, help_text="Enter the book's language") 1a

22 

23 class Meta: 1a

24 ordering: ClassVar[list[str]] = ["name"] 1a

25 

26 def __str__(self): 1a

27 return self.name 1a

28 

29 

30class Book(models.Model): 1a

31 title = models.CharField(max_length=200) 1a

32 author = models.ForeignKey("Author", on_delete=models.SET_NULL, null=True) 1a

33 pages = models.IntegerField(null=True, blank=True, help_text="Number of Pages") 1a

34 publisher = models.CharField(max_length=40, blank=True) 1a

35 pubdate = models.DateField(null=True, blank=True, help_text="Date Published") 1a

36 summary = models.TextField( 1a

37 max_length=1000, help_text="Enter a brief description of the book." 

38 ) 

39 isbn = models.CharField( 1a

40 "ISBN", 

41 max_length=13, 

42 unique=True, 

43 help_text="13 Character <a href='https://www.isbn-international.org/content/what-isbn'>ISBN number</a>", 

44 ) 

45 genre = models.ManyToManyField(Genre, help_text="Select a genre for this book") 1a

46 language = models.ForeignKey("Language", on_delete=models.SET_NULL, null=True) 1a

47 cover = models.ImageField(upload_to="covers/", blank=True) 1a

48 

49 class Meta: 1a

50 ordering: ClassVar[list[str]] = ["title", "author"] 1a

51 

52 def __str__(self): 1a

53 return self.title 1a

54 

55 def get_absolute_url(self): 1a

56 return reverse("book-detail", args=[str(self.id)]) 1a

57 

58 def display_genre(self): 1a

59 """Creates a string for Genre. Required for Admin display""" 

60 return ", ".join([genre.name for genre in self.genre.all()[:3]]) 

61 

62 display_genre.short_description = "Genre" 1a

63 

64 

65class BookInstance(models.Model): 1a

66 """Represents a specific copy of a book""" 

67 

68 id = models.UUIDField( 1a

69 primary_key=True, 

70 default=uuid.uuid4, 

71 help_text="Unique ID for this book across whole library", 

72 ) 

73 book = models.ForeignKey("Book", on_delete=models.RESTRICT, null=True) 1a

74 imprint = models.CharField(max_length=200) 1a

75 due_back = models.DateField(null=True, blank=True) 1a

76 borrower = models.ForeignKey( 1a

77 get_user_model(), on_delete=models.SET_NULL, null=True, blank=True 

78 ) 

79 

80 LOAN_STATUS = ( 1a

81 ("m", "Maintenance"), 

82 ("o", "On Loan"), 

83 ("a", "Available"), 

84 ("r", "Reserved"), 

85 ) 

86 

87 status = models.CharField( 1a

88 max_length=1, 

89 choices=LOAN_STATUS, 

90 blank=True, 

91 default="m", 

92 help_text="Book Availability", 

93 ) 

94 

95 class Meta: 1a

96 ordering: ClassVar[list[str]] = ["due_back"] 1a

97 permissions = (("can_mark_returned", "Set book as returned"),) 1a

98 

99 def __str__(self): 1a

100 return f"{self.id} ({self.book.title})" 

101 

102 @property 1a

103 def is_overdue(self): 1a

104 return bool(self.due_back and date.today() > self.due_back) 1a

105 

106 

107class Author(models.Model): 1a

108 first_name = models.CharField(max_length=100) 1a

109 middle_name = models.CharField( 1a

110 max_length=100, blank=True, help_text="Middle Name(initial)" 

111 ) 

112 last_name = models.CharField(max_length=100) 1a

113 date_of_birth = models.DateField(null=True, blank=True) 1a

114 date_of_death = models.DateField("Died", null=True, blank=True) 1a

115 

116 class Meta: 1a

117 ordering: ClassVar[list[str]] = ["last_name", "first_name"] 1a

118 

119 def __str__(self): 1a

120 if self.middle_name is not None: 120 ↛ 123line 120 didn't jump to line 123 because the condition on line 120 was always true1a

121 return f"{self.last_name}, {self.first_name} {self.middle_name}" 1a

122 else: 

123 return f"{self.last_name}, {self.first_name}" 

124 

125 def get_absolute_url(self): 1a

126 return reverse("author-detail", args=[str(self.id)]) 1a