Skip to content
Open

My #5

Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 32 additions & 2 deletions acme_project/acme_project/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,13 @@

DEBUG = True

ALLOWED_HOSTS = []
ALLOWED_HOSTS = [
'localhost',
'127.0.0.1',
# Когда проект будет опубликован и станет доступен для пользователей,
# в этот список нужно будет добавить и адреса домена, где он будет размещён,
# например 'acme.not' и 'www.acme.not'
]

INSTALLED_APPS = [
'django.contrib.admin',
Expand All @@ -17,6 +23,11 @@
'django.contrib.staticfiles',
'birthday.apps.BirthdayConfig',
'pages.apps.PagesConfig',
'django_bootstrap5',
# Регистрируем новое приложение в проекте:
# обязательно ниже, чем django.contrib.staticfiles.
'debug_toolbar',

]

MIDDLEWARE = [
Expand All @@ -27,6 +38,7 @@
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
'debug_toolbar.middleware.DebugToolbarMiddleware',
]

ROOT_URLCONF = 'acme_project.urls'
Expand Down Expand Up @@ -79,10 +91,28 @@

USE_I18N = True

USE_L10N = True
USE_L10N = False

USE_TZ = True

STATIC_URL = '/static/'

DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField'

MEDIA_ROOT = BASE_DIR / 'media'

# Подключаем бэкенд filebased.EmailBackend:
EMAIL_BACKEND = 'django.core.mail.backends.filebased.EmailBackend'
# Указываем директорию, в которую будут сохраняться файлы писем:
EMAIL_FILE_PATH = BASE_DIR / 'sent_emails'
# После логина будем перенаправлять пользователя на главную страницу — homepage
LOGIN_REDIRECT_URL = 'pages:homepage'
# Адрес страницы логина можно указать в настройках проекта, в константе LOGIN_URL
LOGIN_URL = 'login'
# Имя view-функции, обрабатывающей ошибку 403, указывается в settings.py, в константе CSRF_FAILURE_VIEW
CSRF_FAILURE_VIEW = 'core.views.csrf_failure'
# Добавьте в settings.py эту константу, чтобы DjDT знал,
# запросы с каких IP он должен обрабатывать.
INTERNAL_IPS = [
'127.0.0.1',
]
32 changes: 31 additions & 1 deletion acme_project/acme_project/urls.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,38 @@
# Импортируем настройки проекта.
from django.conf import settings
# Добавьте новые строчки с импортами классов.
from django.contrib.auth.forms import UserCreationForm
from django.views.generic.edit import CreateView
# Импортируем функцию, позволяющую серверу разработки отдавать файлы.
from django.conf.urls.static import static
from django.contrib import admin
from django.urls import include, path
from django.urls import include, path, reverse_lazy


handler404 = 'core.views.page_not_found'

urlpatterns = [
path('', include('pages.urls')),
path('admin/', admin.site.urls),
path('birthday/', include('birthday.urls')),
path('auth/', include('django.contrib.auth.urls')),
path(
'auth/registration/',
CreateView.as_view(
template_name='registration/registration_form.html',
form_class=UserCreationForm,
success_url=reverse_lazy('pages:homepage'),
),
name='registration',
),
# В конце добавляем к списку вызов функции static.
]

if settings.DEBUG:
import debug_toolbar
# Добавить к списку urlpatterns список адресов из приложения debug_toolbar:
urlpatterns += (path('__debug__/', include(debug_toolbar.urls)),)

# Подключаем функцию static() к urlpatterns:
urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)

5 changes: 5 additions & 0 deletions acme_project/birthday/admin.py
Original file line number Diff line number Diff line change
@@ -1 +1,6 @@
from django.contrib import admin

from .models import Birthday, Tag

admin.site.register(Birthday)
admin.site.register(Tag)
64 changes: 64 additions & 0 deletions acme_project/birthday/forms.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
from django import forms
# Импортируем класс ошибки валидации.
from django.core.exceptions import ValidationError
# Импорт функции для отправки почты.
from django.core.mail import send_mail

# Импортируем класс модели Birthday и Congratulation.
from .models import Birthday, Congratulation


# Множество с именами участников Ливерпульской четвёрки.
BEATLES = {'Джон Леннон', 'Пол Маккартни', 'Джордж Харрисон', 'Ринго Старр'}


# Для использования формы с моделями меняем класс на forms.ModelForm.
class BirthdayForm(forms.ModelForm):
# Удаляем все описания полей.

# Все настройки задаём в подклассе Meta.
class Meta:
# Указываем модель, на основе которой должна строиться форма.
model = Birthday
# Берем все поля, за исключением author
exclude = ('author',)
widgets = {
'birthday': forms.DateInput(attrs={'type': 'date'})
}

def clean_first_name(self):
# Получаем значение имени из словаря очищенных данных.
first_name = self.cleaned_data['first_name']
# Разбиваем полученную строку по пробелам
# и возвращаем только первое имя.
return first_name.split()[0]

def clean(self):
# Вызов родительского метода clean.
super().clean()
# Получаем имя и фамилию из очищенных полей формы.
first_name = self.cleaned_data['first_name']
last_name = self.cleaned_data['last_name']
# Проверяем вхождение сочетания имени и фамилии во множество имён.
if f'{first_name} {last_name}' in BEATLES:
# Отправляем письмо, если кто-то представляется
# именем одного из участников Beatles.
send_mail(
subject='Another Beatles member',
message=f'{first_name} {last_name} пытался опубликовать запись!',
from_email='[email protected]',
recipient_list=['[email protected]'],
fail_silently=True,
)
raise ValidationError(
'Мы тоже любим Битлз, но введите, пожалуйста, настоящее имя!'
)


class CongratulationForm(forms.ModelForm):

class Meta:
model = Congratulation
fields = ('text',)


23 changes: 23 additions & 0 deletions acme_project/birthday/migrations/0001_initial.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# Generated by Django 3.2.16 on 2024-08-02 16:43

from django.db import migrations, models


class Migration(migrations.Migration):

initial = True

dependencies = [
]

operations = [
migrations.CreateModel(
name='Birthday',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('first_name', models.CharField(max_length=20, verbose_name='Имя')),
('last_name', models.CharField(blank=True, help_text='Необязательное поле', max_length=20, verbose_name='Фамилия')),
('birthday', models.DateField(verbose_name='Дата рождения')),
],
),
]
28 changes: 28 additions & 0 deletions acme_project/birthday/migrations/0002_auto_20240804_1009.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
# Generated by Django 3.2.16 on 2024-08-04 07:09

import birthday.validators
from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [
('birthday', '0001_initial'),
]

operations = [
migrations.AddField(
model_name='birthday',
name='image',
field=models.ImageField(blank=True, upload_to='birthdays_images', verbose_name='Фото'),
),
migrations.AlterField(
model_name='birthday',
name='birthday',
field=models.DateField(validators=[birthday.validators.real_age], verbose_name='Дата рождения'),
),
migrations.AddConstraint(
model_name='birthday',
constraint=models.UniqueConstraint(fields=('first_name', 'last_name', 'birthday'), name='Unique person constraint'),
),
]
21 changes: 21 additions & 0 deletions acme_project/birthday/migrations/0003_birthday_author.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# Generated by Django 3.2.16 on 2024-08-09 22:18

from django.conf import settings
from django.db import migrations, models
import django.db.models.deletion


class Migration(migrations.Migration):

dependencies = [
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
('birthday', '0002_auto_20240804_1009'),
]

operations = [
migrations.AddField(
model_name='birthday',
name='author',
field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL, verbose_name='Автор записи'),
),
]
29 changes: 29 additions & 0 deletions acme_project/birthday/migrations/0004_congratulation.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
# Generated by Django 3.2.16 on 2024-08-11 08:09

from django.conf import settings
from django.db import migrations, models
import django.db.models.deletion


class Migration(migrations.Migration):

dependencies = [
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
('birthday', '0003_birthday_author'),
]

operations = [
migrations.CreateModel(
name='Congratulation',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('text', models.TextField(verbose_name='Текст поздравления')),
('created_at', models.DateTimeField(auto_now_add=True)),
('author', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)),
('birthday', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='congratulations', to='birthday.birthday')),
],
options={
'ordering': ('created_at',),
},
),
]
25 changes: 25 additions & 0 deletions acme_project/birthday/migrations/0005_auto_20240812_1536.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
# Generated by Django 3.2.16 on 2024-08-12 12:36

from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [
('birthday', '0004_congratulation'),
]

operations = [
migrations.CreateModel(
name='Tag',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('tag', models.CharField(max_length=20, verbose_name='Тег')),
],
),
migrations.AddField(
model_name='birthday',
name='tags',
field=models.ManyToManyField(blank=True, help_text='Удерживайте Ctrl для выбора нескольких вариантов', to='birthday.Tag', verbose_name='Теги'),
),
]
61 changes: 61 additions & 0 deletions acme_project/birthday/models.py
Original file line number Diff line number Diff line change
@@ -1 +1,62 @@
from django.db import models
from django.contrib.auth import get_user_model

# Импортируется функция-валидатор.
from .validators import real_age
# Импортируем функцию reverse() для получения ссылки на объект.
from django.urls import reverse

User = get_user_model()


class Tag(models.Model):
tag = models.CharField('Тег', max_length=20)

# Переопределяем метод:
def __str__(self):
return self.tag


class Birthday(models.Model):
first_name = models.CharField('Имя', max_length=20)
last_name = models.CharField(
'Фамилия', max_length=20, help_text='Необязательное поле', blank=True
)
# Валидатор указывается в описании поля.
birthday = models.DateField('Дата рождения', validators=(real_age,))
image = models.ImageField('Фото', upload_to='birthdays_images', blank=True)
author = models.ForeignKey(
User, verbose_name='Автор записи', on_delete=models.CASCADE, null=True
)
tags = models.ManyToManyField(
Tag,
verbose_name='Теги',
blank=True,
help_text='Удерживайте Ctrl для выбора нескольких вариантов'
)

class Meta:
constraints = (
models.UniqueConstraint(
fields=('first_name', 'last_name', 'birthday'),
name='Unique person constraint',
),
)

def get_absolute_url(self):
# С помощью функции reverse() возвращаем URL объекта.
return reverse('birthday:detail', kwargs={'pk': self.pk})


class Congratulation(models.Model):
text = models.TextField('Текст поздравления')
birthday = models.ForeignKey(
Birthday,
on_delete=models.CASCADE,
related_name='congratulations',
)
created_at = models.DateTimeField(auto_now_add=True)
author = models.ForeignKey(User, on_delete=models.CASCADE)

class Meta:
ordering = ('created_at',)
8 changes: 7 additions & 1 deletion acme_project/birthday/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,11 @@
app_name = 'birthday'

urlpatterns = [
path('', views.birthday, name='create'),
path('', views.BirthdayCreateView.as_view(), name='create'),
path('list/', views.BirthdayListView.as_view(), name='list'),
path('<int:pk>/', views.BirthdayDetailView.as_view(), name='detail'),
path('login_only/', views.simple_view),
path('<int:pk>/edit/', views.BirthdayUpdateView.as_view(), name='edit'),
path('<int:pk>/delete/', views.BirthdayDeleteView.as_view(), name='delete'),
path('<int:pk>/comment/', views.add_comment, name='add_comment'),
]
Loading