Sergei_Erjemin
@Sergei_Erjemin
Улыбайся, будь самураем...

Как в Django переопределить метод «чтение» только в/для админки?

Я знаю, что так не делают, но для одного своего пет-проекта хочу странного: хранить jinja-шаблоны в базе (и уметь править их через админку).

Есть примерно такая модель (в models.py):
class TbTemplate(models.Model):
    """ Шаблоны страниц """
    szFileName = models.CharField(
        primary_key=True,
        db_index=True,
        unique=True, 
        verbose_name="Имя шаблона"
        )
    szJinjaCode = models.TextField(
        verbose_name='Шаблон',
        help_text='Код шаблона (jinja2)'
    )
    szDescription = models.CharField(
        max_length=100,
        verbose_name='Описание'
    )

    def __unicode__(self):
        return f"{self.szFileName} ({self.szDescription})"

    def __str__(self):
        return self.__unicode__()

    class Meta:
        verbose_name = '[…Шаблон]'
        verbose_name_plural = '[…Шаблоны]'

Далее, во view.py можно сделать что-то типа такого:
# -*- coding: utf-8 -*-
from django.http import HttpRequest, HttpResponse
from django.template.loader import render_to_string
from web.models import TbTemplate

def something(request: HttpRequest, template: str) -> HttpResponse:
    """
    :param request: входящий http-запрос
    :param template: имя шаблона
    :return response: исходящий http-ответ
    """
    to_template = {}
    # ...
    # ... что-то делаем
    # ...
    tmpl = TbTemplate.objects.get(pk=template)
    html = render_to_string(tmpl.szJinjaCode, to_template)
    return HttpResponse(html)


И все будет работать. Шаблоны будут доступны к редактированию через через админку (конечно, надо навесить виджет типа mirror для подсветки синтаксиса и т.п.)...

Но хочется более странного. Например, использовать в jinja-шаблонах конструкции типа: {% include "something_template.jinja2" %} ... А для этого надо чтобы шаблоны были не только в базе, но и хранились как файлы в соответствующей папке. Кроме того, шаблоны проще создавать-редактировать в умных IDE, а доступ к шаблонам через админку удобнее только для косметических правок (кроме того хочется автоматически "передергивать" uWSGI при обновлении... переопределив метод save() в модели можно добавить модификацию файла touch для uWSGI и всё начнет "передергиваться" автоматически при косметическом изменении шаблона).

Так вот, получается нужно как-то перехватить метод «чтение» в/для админки. Чтобы если в админке открывают на редактирование шаблонв таблице TbTemplate , то для поля szJinjaCode оно читалось не из базы, а из соответствующего файла szFileName. Как такое сделать?

P.S. Вторичный вопрос (на который пока можно не отвечать, но вдруг знаете): как сделать так, чтобы при инициализационной django-миграции:
python manage.py makemigrations web
python manage.py migrate
были просмотрены папки с шаблонами и для существующих шаблонов были созданы соответствующие записи в TbTemplate ?
  • Вопрос задан
  • 94 просмотра
Решения вопроса 1
Sergei_Erjemin
@Sergei_Erjemin Автор вопроса
Улыбайся, будь самураем...
Делается в два действия:

Во-первых,
в models.py переопределяем метод save() для нашей модели TbTemplate. Заодно можем переопределить метод delete(), чтобы он ничего не удалял (или наоборот удалял не только запись в БД но и соответствующий файл... или удалял запись в БД, а соответствующий файл переименовывал...). Получим такую модель:
# -*- coding: utf-8 -*-
from django.db import models
from my_app.settings import *


class TbTemplate(models.Model):
    """ Шаблоны """
    szFileName = models.CharField(
        primary_key=True,
        db_index=True,
        unique=True, 
        verbose_name="Имя шаблона"
        )
    szJinjaCode = models.TextField(
        verbose_name='Шаблон',
        help_text='Код шаблона (jinja2)'
    )
    szDescription = models.CharField(
        max_length=100,
        verbose_name='Описание'
    )

    def __unicode__(self):
        return f"{self.szFileName} ({self.szDescription})"

    def __str__(self):
        return self.__unicode__()

    # переопределяем save() для записи шаблонов не только в ДБ, но и в файл
    def save(self, *args, **kwargs):
        with open(TEMPLATES_DIR / self.szFileName, "w+", encoding="utf-8") as tmplt_file:
            tmplt_file.write(self.szJinjaCode)
        super(TbTemplate, self).save(*args, **kwargs)
        # TODO: для продакшн, возможно, нужно добавить "дёргание" touch_reload и "моргнуть" uWSGI

    # переопределяем метод delete() (пока, не удаляется)
    def delete(self, *args, **kwargs):
        pass
        # super(TbTemplate, self).delete(*args, **kwargs)

    class Meta:
        verbose_name = '[…Шаблон]'
        verbose_name_plural = '[…Шаблоны]'


Теперь, при изменении и создании шаблона в базе создастся соответствующий файл...

Во-вторых,
в файле admin.py при определении класса admin.ModelAdmin для управления моделью TbTemplate нужно переопределить метод get_fields() который отвечает за получения полей в форму админки (пришлось искать метод тупо пробуя кучу схожих которые делают что-то похожее, но не то). В результате получаем примерно вот такой вот admin.py:
# -*- coding: utf-8 -*-
from django.contrib import admin
from web.models import TbTemplate
from my_app.settings import *

class AdminTemplate(admin.ModelAdmin):
    search_fields = ['szFileName', 'szDescription', 'szJinjaCode']
    list_display = ('szFileName', 'szDescription')
    list_display_links = ('szFileName', 'szDescription', )
    empty_value_display = '<b style=\'color:red;\'>—//—</b>'
    actions_on_top = False
    actions_on_bottom = True

    def get_fields(self, request, obj=None):
        try:
            with open(Path(TEMPLATES_DIR) / obj.szFileName, "r", encoding="utf-8") as template:
                obj.szJinjaCode = template.read()
        except (AttributeError, FileNotFoundError, TypeError):
            pass
        return ['szFileName', 'szDescription', 'szJinjaCode']

admin.site.register(TbTemplate, AdminTemplate)


Теперь, если какие-то внешние силы изменят файл шаблона (или кто-то поменяет код шаблона в БД в обход админки) , то при открытии шаблона на редактирование из админке, данные будут получены из файла.

Все.

UPD: в settnig.py проекта надо добавить что-то типа:
TEMPLATES_DIR = BASE_DIR / 'templates-jinja2'
Чтоб модель и админка знали в какой каталог лить файлы шаблонов.
Ответ написан
Комментировать
Пригласить эксперта
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Войти через центр авторизации
Похожие вопросы