Django 24. Связь модели файла и поста
В этом посте настроим связь между моделью поста и моделью хранящей файл. Сделаем генерацию цифрового кода для файла, а также добавим отображение файла на странице поста.
Дополнительные материалы
Для скачивания материалов необходимо войти или зарегистрироваться
Файлы также можно получить в Telegram-боте по коду: 187343
Реклама
У нас есть модель для хранения файлов и модель поста.
Для того чтобы у поста, можно было выбирать файл, а у файла был доступ к полям поста, их нужно связать.
Внешний ключ.
Откроем файл 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
Все статьи