models.py 6.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117
  1. from django.db import models
  2. from django.contrib.auth.models import AbstractUser
  3. from django.db.models.signals import post_save
  4. from .utilities import get_timestamp_path, send_new_comment_notification
  5. class AdvUser(AbstractUser):
  6. is_activated = models.BooleanField(default=True, db_index=True, verbose_name='Прошел активацию?')
  7. send_messages = models.BooleanField(default=True, verbose_name='Оповещение на email о новых комментариях?')
  8. # При удалении пользователя удаляем и его объявления
  9. def delete(self, *args, **kwargs):
  10. for ad in self.ad_set.all():
  11. ad.delete()
  12. super().delete(*args, **kwargs)
  13. class Meta(AbstractUser.Meta):
  14. pass
  15. class Rubric(models.Model):
  16. # Базовая модель, в которой хранятся и главные рубрики, и подрубрики
  17. name = models.CharField(max_length=30, db_index=True, unique=True, verbose_name='Название')
  18. order = models.SmallIntegerField(default=0, db_index=True, verbose_name='Порядок следования')
  19. super_rubric = models.ForeignKey('SuperRubric', on_delete=models.PROTECT, null=True, blank=True, verbose_name='Главная рубрика')
  20. class SuperRubricManager(models.Manager):
  21. # Обработка только главных рубрик
  22. def get_queryset(self):
  23. return super().get_queryset().filter(super_rubric__isnull=True)
  24. class SuperRubric(Rubric):
  25. objects = SuperRubricManager()
  26. def __str__(self):
  27. # Метод генерит строковое название Главной рубрики
  28. return self.name
  29. class Meta:
  30. proxy = True
  31. ordering = ('order', 'name')
  32. verbose_name = 'Главная рубрика'
  33. verbose_name_plural = 'Главные рубрики'
  34. class SubRubricManager(models.Manager):
  35. def get_queryset(self):
  36. return super().get_queryset().filter(super_rubric__isnull=False)
  37. class SubRubric(Rubric):
  38. objects = SubRubricManager()
  39. def __str__(self):
  40. return '%s - %s' % (self.super_rubric.name, self.name)
  41. class Meta:
  42. proxy = True
  43. ordering = ('super_rubric__order', 'super_rubric__name', 'order', 'name')
  44. verbose_name = 'Подрубрика'
  45. verbose_name_plural = 'Подрубрики'
  46. # Класс самих объявлений
  47. class Ad(models.Model):
  48. rubric = models.ForeignKey(SubRubric, on_delete=models.PROTECT, verbose_name='Рубрика') # Запрет каскадного удаления
  49. title = models.CharField(max_length=40, verbose_name='Товар')
  50. content = models.TextField(verbose_name='Описание')
  51. price = models.FloatField(default=0, verbose_name='Цена')
  52. contacts = models.TextField(verbose_name='Контакты')
  53. image = models.ImageField(blank=True, upload_to=get_timestamp_path, verbose_name='Изображение')
  54. # разрешаем каскадное удаление. Т.е. при удалении объявления будут уничтожены все относящиеся к нему Дополнительные Изображения.
  55. # Это действие выполнит не Django, а СУБД. Т.е. физического удаления с диска файлов Изображений не произойдет.
  56. author = models.ForeignKey(AdvUser, on_delete=models.CASCADE, verbose_name='Автор объявления')
  57. is_active = models.BooleanField(default=True, db_index=True, verbose_name='Выводить в списке объявлений?')
  58. created_at = models.DateTimeField(auto_now_add=True, db_index=True, verbose_name='Опубликовано')
  59. # Перед удалением текущей записи мы перебираем и вызовом метода delete() удаляем все связанные Дополнительные Изображения.
  60. # При вызове метода delete() возникает сигнал post_delete, обрабатываемый приложением django_cleanup, которое удалит файлы Изображений физически с диска.
  61. def delete(self, *args, **kwargs):
  62. for ai in self.additionalimage_set.all():
  63. ai.delete()
  64. super().delete(*args, **kwargs)
  65. class Meta:
  66. verbose_name_plural = 'Объявления'
  67. verbose_name = 'Объявление'
  68. ordering = ['-created_at']
  69. # Класс Дополнительных изображений
  70. class AdditionalImage(models.Model):
  71. # Объявление, к которому относится Изображение
  72. ad = models.ForeignKey(Ad, on_delete=models.CASCADE, verbose_name='Объявление')
  73. image = models.ImageField(upload_to=get_timestamp_path, verbose_name='Изображение')
  74. class Meta:
  75. verbose_name_plural = 'Дополнительные изображения'
  76. verbose_name = 'Дополнительное изображение'
  77. #Класс Комментариев
  78. class Comment(models.Model):
  79. ad = models.ForeignKey(Ad, on_delete=models.CASCADE, verbose_name='Объявление')
  80. author = models.CharField(max_length=30, verbose_name='Автор')
  81. content = models.TextField(verbose_name='Комментарий')
  82. is_active = models.BooleanField(default=True, db_index=True, verbose_name='Выводить на экран?')
  83. created_at = models.DateTimeField(auto_now_add=True, db_index=True, verbose_name='Опубликован')
  84. class Meta:
  85. verbose_name_plural = 'Комментарии'
  86. verbose_name = 'Комментарий'
  87. ordering = ['created_at'] # Сортировка по увеличению временной отметки, т.е. более старые комментарии будут располагаться в начале списка, а более новые - в конце.
  88. # Привяжем к сигналу post_save обработчик, вызывающий функцию send_new_comment_notification
  89. # после добавления комментария
  90. def post_save_dispatcher(sender, **kwargs):
  91. author = kwargs['instance'].ad.author
  92. if kwargs['created'] and author.send_messages:
  93. send_new_comment_notification(kwargs['instance'])
  94. post_save.connect(post_save_dispatcher, sender=Comment)