Skip to content

Instantly share code, notes, and snippets.

@sunmeat
Created May 12, 2026 09:26
Show Gist options
  • Select an option

  • Save sunmeat/25cecce643e41278fbe2fc15636ccd41 to your computer and use it in GitHub Desktop.

Select an option

Save sunmeat/25cecce643e41278fbe2fc15636ccd41 to your computer and use it in GitHub Desktop.
збереження файлів та зображень на сервері (MEDIA_ROOT / MEDIA_URL)
settings.py:
import os # !!!
import posixpath
from pathlib import Path
BASE_DIR = Path(__file__).resolve().parent.parent
SECRET_KEY = '545bb8a0-f016-4d1b-8b4f-22bd108e577a'
DEBUG = True
ALLOWED_HOSTS = []
INSTALLED_APPS = [
'app',
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
]
MIDDLEWARE = [
'django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
]
ROOT_URLCONF = 'Forms.urls'
TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': [],
'APP_DIRS': True,
'OPTIONS': {
'context_processors': [
'django.template.context_processors.debug',
'django.template.context_processors.request',
'django.contrib.auth.context_processors.auth',
'django.contrib.messages.context_processors.messages',
],
},
},
]
WSGI_APPLICATION = 'Forms.wsgi.application'
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.sqlite3',
'NAME': BASE_DIR / 'db.sqlite3',
}
}
AUTH_PASSWORD_VALIDATORS = [
{'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator'},
{'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator'},
{'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator'},
{'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator'},
]
LANGUAGE_CODE = 'uk'
TIME_ZONE = 'Europe/Kiev'
USE_I18N = True
USE_TZ = True
# !!! STATIC & MEDIA !!!
STATIC_URL = '/static/'
STATIC_ROOT = BASE_DIR / 'static'
# медіа-файли (завантажені користувачами)
MEDIA_URL = '/media/'
MEDIA_ROOT = BASE_DIR / 'media'
# DEFAULT_AUTO_FIELD - це налаштування, яке визначає тип поля, що використовується за замовчуванням для автоматичних первинних ключів у моделях Django
# у цьому випадку встановлено 'django.db.models.BigAutoField', що означає, що всі автоматичні первинні ключі будуть використовувати тип BigAutoField, який є 64-бітовим цілим числом і може зберігати значно більше значень, ніж стандартний AutoField (32-бітовий)
# це корисно для проєктів, які можуть мати велику кількість записів у БД
DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField'
================================================================================================================
views.py:
import os
from datetime import datetime
from pathlib import Path
from django.http import HttpRequest
from django.shortcuts import render, redirect
from django.contrib import messages
from .forms import ContactForm
def contact(request):
"""Обробка форми відправки резюме"""
if request.method == 'POST':
form = ContactForm(request.POST, request.FILES)
if form.is_valid():
resume = form.cleaned_data.get('resume_file')
avatar = form.cleaned_data.get('avatar')
# !!! збереження файлів на сервері (у папках media/resumes та media/avatars)
saved_files = []
# створюються папки, якщо їх немає
media_root = Path('media')
(media_root / 'resumes').mkdir(parents=True, exist_ok=True)
(media_root / 'avatars').mkdir(parents=True, exist_ok=True)
# збереження резюме
if resume:
# унікальне ім'я
ext = os.path.splitext(resume.name)[1].lower()
new_filename = f"resume_{datetime.now().strftime('%Y%m%d_%H%M%S')}{ext}"
file_path = media_root / 'resumes' / new_filename
with open(file_path, 'wb+') as destination:
for chunk in resume.chunks():
destination.write(chunk)
saved_files.append(f"Резюме: {new_filename}")
# збереження аватарки
if avatar:
ext = os.path.splitext(avatar.name)[1].lower()
new_filename = f"avatar_{datetime.now().strftime('%Y%m%d_%H%M%S')}{ext}"
file_path = media_root / 'avatars' / new_filename
with open(file_path, 'wb+') as destination:
for chunk in avatar.chunks():
destination.write(chunk)
saved_files.append(f"Фото: {new_filename}")
# вивід в термінал
print("\n" + "="*80)
print("НОВЕ РЕЗЮМЕ ОТРИМАНО!")
print("="*80)
print(f"Дата та час: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}")
print("-" * 80)
for field_name, value in form.cleaned_data.items():
label = form.fields[field_name].label or field_name
print(f"{label:35} : {value}")
if saved_files:
print("-" * 80)
for file_info in saved_files:
print(file_info)
print("="*80 + "\n")
messages.success(request, 'Резюме успішно надіслано! Дякуємо.')
return redirect('contact') # залишаємо на сторінці форми
else:
messages.error(request, 'Будь ласка, виправте помилки у формі.')
else:
form = ContactForm()
return render(request, 'app/contact.html', {
'form': form,
'title': 'Відправка резюме',
'year': datetime.now().year,
})
def home(request):
return render(request, 'app/index.html', {
'title': 'Home Page',
'year': datetime.now().year,
})
def about(request):
return render(request, 'app/about.html', {
'title': 'About',
'message': 'Your application description page.',
'year': datetime.now().year,
})
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment