Cat

Django 24. Связь модели файла и поста

В этом посте настроим связь между моделью поста и моделью хранящей файл. Сделаем генерацию цифрового кода для файла, а также добавим отображение файла на странице поста.

Все статьи

Icon Link

Дополнительные материалы

Icon Link

Реклама

Icon Link
Сайт на Django proDream 06 Сентябрь 2023 Просмотров: 763

У нас есть модель для хранения файлов и модель поста.
Для того чтобы у поста, можно было выбирать файл, а у файла был доступ к полям поста, их нужно связать.

Внешний ключ.

Откроем файл models.py и пропишем внешний ключ в модели поста PostModel к модели файла.

Создадим переменную file, сделав её полем Один-к-Одному(OneToOneField). В параметрах укажем:

  • Модель файла.
  • Действия при удалении файла. Если удалим файл, пост не будет удалён, значение поля обнулится.
  • Разрешение быть пустым и не обязательным полем.
  • Название поля.
  • "Зависимое" имя поля.
    file = models.OneToOneField(PostFilesModel,
                             on_delete=models.SET_NULL,
                             null=True,
                             blank=True,
                             verbose_name="Файл",
                             related_name="post")

 

Поле related_name позволит обращаться из объекта файла к связанному объекту поста.

 

Генерация кода для файла.

Отвлечёмся от основной темы поста.
Не уходя из файла models.py добавим генерацию кода для файла.

В классе PostFilesModel создадим метод генерации уникального кода и переопределим метод сохранения модели. Должно получиться так:

import random


    def save(self, *args, **kwargs):
        if not self.code:
            self.code = self.generate_unique_code()
        super().save(*args, **kwargs)

    @staticmethod
    def generate_unique_code():
        code = random.randint(100000, 999999)
        while PostFilesModel.objects.filter(code=code).exists():
            code = random.randint(100000, 999999)
        return code

 

Не обязательно, но поле code можно сделать полем "только для чтения".
Перейдём в файл admin.py и найдём класс PostFilesAdmin. Добавим в него всего одно поле:

    readonly_fields = ('code',)

 

Теперь код будет генерироваться при создании объекта, а в панели администратора нельзя будет его изменить.

Применим миграции:

python manage.py makemigrations
python manage.py migrate

 

Добавление в шаблон.

Возвращаемся к основной теме поста.

Для того чтобы отобразить ссылку на файл, в шаблоне необходимо обращаться к полю файл в модели файла и запрашивать его путь.

<h2 class="mb-3">Дополнительные материалы</h2>  
<a href="{{ post.file.file.url }}" class="mb-1">Скачать материалы и исходный код с сайта.</a>  
<p class="mt-1">Или в <a href="https://t.me/press_any_button_bot">Telegram-боте</a> по коду: <b>{{ post.file.code }}</b></p>

 

Изменение сериализатора.

Изменим сериализатор, чтобы он возвращал не только название файла и путь до него, но и поля из поста, например, ссылку на пост.

Добавим два новых поля:

  • post_url - возвращающий ссылку на пост, для этого задействуем метод SerializerMethodField, поскольку получение полного URL до поста, это метод get_absolute_url. Параметр read_only=True позволяет, в случае если у файла нет связанного поста, получить None вместо исключения.
  • post_telegram_link - возвращающий ссылку на пост в Telegram.

 

И метод, получающий URL поста и добавляющий к нему UTM-метку для сбора статистики переходов.

В методе используем try-except, также, чтобы избежать исключения, если у файла нет связанного поста.

 

Полный код сериализатора:

class FileModelSerializer(serializers.ModelSerializer):
    file_path = serializers.SerializerMethodField()
    post_url = serializers.SerializerMethodField()
    post_telegram_link = serializers.StringRelatedField(source='post.telegram_link', read_only=True)

    class Meta:
        model = models.PostFilesModel
        fields = ['title', 'file_path', 'post_url', 'post_telegram_link']

    @staticmethod
    def get_file_path(obj):
        return obj.file.path if obj.file else None

    @staticmethod
    def get_post_url(obj: models.PostFilesModel):
        try:
            url = obj.post.get_absolute_url()
            utm_params = '?utm_source=telegram&utm_medium=bot&utm_campaign=get_file'
            return get_current_site(None).domain + url + utm_params
        except ObjectDoesNotExist:
            return None

 

Автор

    Нет комментариев

    Реклама