Skip to content

Instantly share code, notes, and snippets.

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

  • Save sunmeat/47ecd378c1033f767d6c7fff7a2f2e03 to your computer and use it in GitHub Desktop.

Select an option

Save sunmeat/47ecd378c1033f767d6c7fff7a2f2e03 to your computer and use it in GitHub Desktop.
django.forms.ModelForm
models.py:
import os
from django.db import models
from django.core.validators import MinLengthValidator
from datetime import datetime
def resume_upload_path(instance, filename):
"""Генерує шлях для резюме"""
ext = os.path.splitext(filename)[1].lower()
new_name = f"resume_{datetime.now().strftime('%Y%m%d_%H%M%S')}{ext}"
return f'resumes/{new_name}'
def avatar_upload_path(instance, filename):
"""Генерує шлях для аватарки"""
ext = os.path.splitext(filename)[1].lower()
new_name = f"avatar_{datetime.now().strftime('%Y%m%d_%H%M%S')}{ext}"
return f'avatars/{new_name}'
class ResumeApplication(models.Model):
"""Модель для зберігання відправлених резюме"""
full_name = models.CharField("ПІБ", max_length=150, validators=[MinLengthValidator(3)])
email = models.EmailField("Email")
phone = models.CharField("Телефон", max_length=20)
resume_file = models.FileField("Резюме", upload_to=resume_upload_path, blank=False)
avatar = models.ImageField("Фото", upload_to=avatar_upload_path, blank=True, null=True)
created_at = models.DateTimeField("Дата відправки", auto_now_add=True)
class Meta: # для налаштування відображення в адмінці та сортування
verbose_name = "Резюме"
verbose_name_plural = "Резюме"
ordering = ['-created_at']
def __str__(self):
return f"{self.full_name} — {self.created_at.strftime('%d.%m.%Y %H:%M')}"
=======================================================================================================
forms.py:
from django import forms
from django.core.validators import MinLengthValidator
import os
from .models import ResumeApplication
def validate_phone(value):
import re
if value:
cleaned = value.replace(' ', '').replace('(', '').replace(')', '').replace('-', '')
if not re.match(r'^\+?380\d{9}$', cleaned):
raise forms.ValidationError("Введіть коректний номер у форматі +380XXXXXXXXX")
class ContactForm(forms.ModelForm):
"""ModelForm для відправки резюме"""
class Meta:
model = ResumeApplication
fields = ['full_name', 'email', 'phone', 'resume_file', 'avatar']
labels = {
'full_name': "ПІБ",
'email': "Email адреса",
'phone': "Номер телефону",
'resume_file': "Файл з резюме",
'avatar': "Фото (аватар)",
}
widgets = {
'full_name': forms.TextInput(attrs={'class': 'form-control', 'placeholder': 'Шевченко Тарас Григорович'}),
'email': forms.EmailInput(attrs={'class': 'form-control', 'placeholder': 'taras.shevchenko@gmail.com'}),
'phone': forms.TextInput(attrs={'class': 'form-control', 'placeholder': '+380 XX XXX XX XX', 'type': 'tel'}),
'resume_file': forms.ClearableFileInput(attrs={'class': 'form-control'}),
'avatar': forms.ClearableFileInput(attrs={'class': 'form-control'}),
}
help_texts = {
'resume_file': "Дозволено: PDF, DOC, DOCX (максимум 10 МБ)",
'avatar': "Дозволено: JPG, JPEG, PNG (максимум 5 МБ)",
}
def clean_resume_file(self):
resume = self.cleaned_data.get('resume_file')
if resume and resume.size > 10 * 1024 * 1024:
raise forms.ValidationError("Файл резюме занадто великий. Максимальний розмір — 10 МБ.")
return resume
def clean_avatar(self):
avatar = self.cleaned_data.get('avatar')
if avatar and avatar.size > 5 * 1024 * 1024:
raise forms.ValidationError("Фото занадто велике. Максимальний розмір — 5 МБ.")
return avatar
=======================================================================================================
views.py:
from datetime import datetime
from django.shortcuts import render, redirect
from django.contrib import messages
from .forms import ContactForm
# !!! зробити міграції після оновлення трьох файлів !!!
# python manage.py makemigrations
# python manage.py migrate
def contact(request):
if request.method == 'POST':
form = ContactForm(request.POST, request.FILES)
if form.is_valid():
application = form.save() # зберігає і в БД, і файли
print("\n" + "="*80)
print("НОВЕ РЕЗЮМЕ ЗБЕРЕЖЕНО В БАЗУ!")
print("="*80)
print(f"ID запису: {application.id}")
print(f"ПІБ: {application.full_name}")
print(f"Email: {application.email}")
print(f"Телефон: {application.phone}")
print(f"Резюме: {application.resume_file}")
if application.avatar:
print(f"Аватар: {application.avatar}")
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