def test_choices(self): types_dict = choices_list( (1, 'T1'), (2, 'T2'), ) assert len(types_dict) == 2 assert types_dict[1] == 'T1' assert types_dict[2] == 'T2' assert list(types_dict.keys()) == [1, 2] ch = get_choices(types_dict) assert len(ch) == 2 assert ch[0][0] == 1 assert ch[0][1] == 'T1' assert ch[1][0] == 2 assert ch[1][1] == 'T2'
class Reference(InheritedModel, RealmBaseModel, CommonEntityModel, ModelWithDiscussions, ModelWithCompiledText): """Модель сущности `Справочник`.""" TYPE_CHAPTER = 1 TYPE_PACKAGE = 2 TYPE_MODULE = 3 TYPE_FUNCTION = 4 TYPE_CLASS = 5 TYPE_METHOD = 6 TYPE_PROPERTY = 7 TYPES = choices_list( (TYPE_CHAPTER, 'Раздел справки'), (TYPE_PACKAGE, 'Описание пакета'), (TYPE_MODULE, 'Описание модуля'), (TYPE_FUNCTION, 'Описание функции'), (TYPE_CLASS, 'Описание класса/типа'), (TYPE_METHOD, 'Описание метода класса/типа'), (TYPE_PROPERTY, 'Описание свойства класса/типа'), ) type = models.PositiveIntegerField( 'Тип статьи', choices=get_choices(TYPES), default=TYPE_CHAPTER, help_text='Служит для структурирования информации. Справочные статьи разных типов могут выглядеть по-разному.') parent = models.ForeignKey( 'self', related_name='children', verbose_name='Родитель', db_index=True, null=True, blank=True, help_text='Укажите родительский раздел. ' 'Например, для модуля можно указать раздел справки, в которому он относится; ' 'для метода — класс.') version_added = models.ForeignKey( Version, related_name='%(class)s_added', verbose_name='Добавлено в', null=True, blank=True, help_text='Версия Python, для которой впервые стала актульна данная статья<br>' '(версия, где впервые появился модуль, пакет, класс, функция).') version_deprecated = models.ForeignKey( Version, related_name='%(class)s_deprecated', verbose_name='Устарело в', null=True, blank=True, help_text='Версия Python, для которой впервые данная статья перестала быть актуальной<br>' '(версия, где модуль, пакет, класс, функция были объявлены устаревшими).') func_proto = models.CharField( 'Прототип', max_length=250, null=True, blank=True, help_text='Для функций/методов. Описание интерфейса, например: <i>my_func(arg, kwarg=None)</i>') func_params = models.TextField( 'Параметры', null=True, blank=True, help_text='Для функций/методов. Описание параметров функции.') func_result = models.CharField( 'Результат', max_length=250, null=True, blank=True, help_text='Для функций/методов. Описание результата, например: <i>int</i>.') history = HistoricalRecords() class Meta: verbose_name = 'Статья справочника' verbose_name_plural = 'Справочник' ordering = ('parent_id', 'title') class Fields: title = { 'verbose_name': 'Название', 'help_text': ('Здесь следует указать название раздела справки ' 'или пакета, модуля, класса, метода, функции и т.п.',) } description = { 'verbose_name': 'Кратко', 'help_text': 'Краткое описание для раздела или пакета, модуля, класса, метода, функции и т.п.', } text_src = { 'verbose_name': 'Описание', 'help_text': 'Подробное описание. Здесь же следует располагать примеры кода.', } notify_on_publish = False autogenerate_slug = True def is_type_callable(self): return self.type in (self.TYPE_METHOD, self.TYPE_FUNCTION) def is_type_method(self): return self.type == self.TYPE_METHOD def is_type_module(self): return self.type == self.TYPE_MODULE def is_type_class(self): return self.type == self.TYPE_CLASS def is_type_chapter(self): return self.type == self.TYPE_CHAPTER @classmethod def get_actual(cls, parent=None, exclude_id=None): qs = cls.objects.published() if parent is not None: qs = qs.filter(parent=parent) if exclude_id is not None: qs = qs.exclude(pk=exclude_id) return qs.order_by('-time_published').all()
class Event(InheritedModel, RealmBaseModel, CommonEntityModel, ModelWithDiscussions, ModelWithCategory, ModelWithCompiledText): """Модель сущности `Событие`.""" SPEC_DEDICATED = 1 SPEC_HAS_SECTION = 2 SPEC_HAS_SOME = 3 SPECS = choices_list( (SPEC_DEDICATED, 'Только Python'), (SPEC_HAS_SECTION, 'Есть секция/отделение про Python'), (SPEC_HAS_SOME, 'Есть упоминания про Python'), ) TYPE_MEETING = 1 TYPE_CONFERENCE = 2 TYPE_LECTURE = 3 TYPE_SPRINT = 4 TYPES = choices_list( (TYPE_MEETING, 'Встреча'), (TYPE_LECTURE, 'Лекция'), (TYPE_CONFERENCE, 'Конференция'), (TYPE_SPRINT, 'Спринт'), ) url = models.URLField('Страница в сети', null=True, blank=True) contacts = models.CharField( 'Контактные лица', null=True, blank=True, max_length=255, help_text=('Контактные лица через запятую, координирующие/устраивающие событие.%s' % ModelWithAuthorAndTranslator._hint_userlink)) place = models.ForeignKey( Place, verbose_name='Место', related_name='events', null=True, blank=True, help_text=('Укажите место проведения мероприятия.<br><b>Конкретный адрес следует указывать в описании.</b><br>' 'Например: «Россия, Новосибирск» или «Новосибирск», но не «Нск».')) specialization = models.PositiveIntegerField('Специализация', choices=get_choices(SPECS), default=SPEC_DEDICATED) type = models.PositiveIntegerField('Тип', choices=get_choices(TYPES), default=TYPE_MEETING) time_start = models.DateTimeField('Начало', null=True, blank=True) time_finish = models.DateTimeField('Завершение', null=True, blank=True, help_text='Дату завершения можно и не указывать.') fee = models.BooleanField('Участие платное', default=False, db_index=True) history = HistoricalRecords() class Meta: verbose_name = 'Событие' verbose_name_plural = 'События' class Fields: description = { 'verbose_name': 'Краткое описание', 'help_text': 'Краткое описание события. %s' % HINT_IMPERSONAL_REQUIRED, } text_src = { 'verbose_name': 'Описание, контактная информация', 'help_text': '%s' % HINT_IMPERSONAL_REQUIRED, } cover = 'Логотип' def save(self, *args, **kwargs): if not self.pk: self.status = self.STATUS_PUBLISHED super().save(*args, **kwargs) def get_display_type(self): return self.TYPES[self.type] def get_display_specialization(self): return self.SPECS[self.specialization] def is_in_past(self): field = self.time_finish or self.time_start if field is None: return None return field < timezone.now() def is_now(self): if not all([self.time_start, self.time_finish]): return False return self.time_start <= timezone.now() <= self.time_finish @classmethod def get_paginator_objects(cls): now = timezone.now().date().isoformat() # Сначала грядущие в порядке приближения, потом прошедшие. return cls.objects.published().extra( select={'in_future': "time_start > '%s'" % now}).order_by('-in_future', 'time_start').all()
class Article(InheritedModel, RealmBaseModel, CommonEntityModel, ModelWithDiscussions, ModelWithCategory, ModelWithCompiledText): """Модель сущности `Статья`.""" LOCATION_INTERNAL = 1 LOCATION_EXTERNAL = 2 LOCATIONS = choices_list( (LOCATION_INTERNAL, 'На этом сайте'), (LOCATION_EXTERNAL, 'На другом сайте'), ) SOURCE_HANDMADE = 1 SOURCE_SCRAPING = 2 SOURCE_RSS = 3 SOURCES = choices_list( (SOURCE_HANDMADE, 'Написана на этом сайте'), (SOURCE_SCRAPING, 'Соскоблена с другого сайта'), (SOURCE_RSS, 'Взята из RSS'), ) source = models.PositiveIntegerField( 'Тип источника', choices=get_choices(SOURCES), default=SOURCE_HANDMADE, help_text='Указывает на механизм, при помощи которого статья появилась на сайте.') location = models.PositiveIntegerField( 'Расположение статьи', choices=get_choices(LOCATIONS), default=LOCATION_INTERNAL, help_text='Статью можно написать прямо на этом сайте, либо сформировать статью-ссылку на внешний ресурс.') url = models.URLField( 'URL статьи', null=True, blank=False, unique=True, help_text='Внешний URL, по которому доступна статья, которой вы желаете поделиться.') published_by_author = models.BooleanField('Я являюсь автором данной статьи', default=True) history = HistoricalRecords() class Meta: verbose_name = 'Статья' verbose_name_plural = 'Статьи' class Fields: description = { 'verbose_name': 'Введение', 'help_text': 'Пара-тройка предложений, описывающих, о чём пойдёт речь в статье.', } linked = { 'verbose_name': 'Связанные статьи', 'help_text': ('Выберите статьи, которые имеют отношение к данной. ' 'Так, например, можно объединить статьи цикла.',) } def is_handmade(self): """Возвращат флаг, указывающий на то, что статья создана на этом сайте. :return: """ return self.source == self.SOURCE_HANDMADE def update_data_from_url(self, url): """Обновляет данные статьи, собирая информация, доступную по указанному URL. :param url: :return: """ result = scrape_page(url) if result is None: raise RemoteSourceError('Не удалось получить данные статьи. Проверьте доступность указанного URL.') self.title = result['title'] self.description = result['content_less'] self.text_src = result['content_more'] self.source = self.SOURCE_SCRAPING