diff --git a/Dockerfile b/Dockerfile
index 8176851..11cf538 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -27,4 +27,4 @@ WORKDIR /src
# root is used as a hotfix for package introspection problem
# https://intellij-support.jetbrains.com/hc/en-us/community/posts/115000373944/comments/7286554132370
-USER root
+USER root
\ No newline at end of file
diff --git a/Makefile b/Makefile
index ef96780..8bfb7b3 100644
--- a/Makefile
+++ b/Makefile
@@ -18,7 +18,7 @@ lint:
# запуск автоматических тестов
test:
- docker compose run app ./manage.py test
+ docker compose run app python manage.py test
# запуск всех функций поддержки качества кода
-all: format lint test
+all: format lint test docs-html
diff --git a/docs/source/code.rst b/docs/source/code.rst
index 9dc7ee3..58558db 100644
--- a/docs/source/code.rst
+++ b/docs/source/code.rst
@@ -5,3 +5,4 @@
portfolio
jobs
blog
+ author
diff --git a/src/__init__.py b/src/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/src/author/__init__.py b/src/author/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/src/author/admin.py b/src/author/admin.py
new file mode 100644
index 0000000..a1fa90a
--- /dev/null
+++ b/src/author/admin.py
@@ -0,0 +1,22 @@
+# Register your models here.
+from django.contrib import admin
+
+from author.models import Author
+
+
+@admin.register(Author)
+class AuthorAdmin(admin.ModelAdmin):
+ list_display = (
+ "resume_url",
+ "github_url",
+ "email",
+ "created_at",
+ "updated_at",
+ )
+
+ search_fields = ("email",)
+
+ list_filter = (
+ "created_at",
+ "updated_at",
+ )
diff --git a/src/author/apps.py b/src/author/apps.py
new file mode 100644
index 0000000..ed859f9
--- /dev/null
+++ b/src/author/apps.py
@@ -0,0 +1,7 @@
+from django.apps import AppConfig
+
+
+class AuthorConfig(AppConfig):
+ default_auto_field = "django.db.models.BigAutoField"
+ name = "author"
+ verbose_name = "Автор"
diff --git a/src/author/migrations/0001_initial.py b/src/author/migrations/0001_initial.py
new file mode 100644
index 0000000..b00b03c
--- /dev/null
+++ b/src/author/migrations/0001_initial.py
@@ -0,0 +1,61 @@
+# Generated by Django 4.1.7 on 2023-02-25 18:43
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+ initial = True
+
+ dependencies = []
+
+ operations = [
+ migrations.CreateModel(
+ name="Author",
+ fields=[
+ (
+ "id",
+ models.BigAutoField(
+ auto_created=True,
+ primary_key=True,
+ serialize=False,
+ verbose_name="ID",
+ ),
+ ),
+ (
+ "created_at",
+ models.DateTimeField(
+ auto_now_add=True, verbose_name="Время создания записи"
+ ),
+ ),
+ (
+ "updated_at",
+ models.DateTimeField(
+ auto_now=True, verbose_name="Время обновления записи"
+ ),
+ ),
+ (
+ "resume_url",
+ models.URLField(
+ help_text="Ссылка на резюме автора", verbose_name="Резюме"
+ ),
+ ),
+ (
+ "github_url",
+ models.URLField(
+ help_text="Ссылка на гитхаб автора", verbose_name="GitHub"
+ ),
+ ),
+ (
+ "email",
+ models.EmailField(
+ help_text="Почта автора", max_length=254, verbose_name="Почта"
+ ),
+ ),
+ ],
+ options={
+ "verbose_name": "Данные об авторе",
+ "verbose_name_plural": "Данные об авторе",
+ },
+ ),
+ ]
diff --git a/src/author/migrations/__init__.py b/src/author/migrations/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/src/author/models.py b/src/author/models.py
new file mode 100644
index 0000000..3ae92a9
--- /dev/null
+++ b/src/author/models.py
@@ -0,0 +1,32 @@
+# Create your models here.
+
+from django.db import models
+
+from base.models import TimeStampMixin
+
+
+class Author(TimeStampMixin):
+ """
+ Модель для хранения данных об авторе.
+ """
+
+ resume_url = models.URLField(
+ verbose_name="Резюме",
+ help_text="Ссылка на резюме автора",
+ )
+ github_url = models.URLField(
+ verbose_name="GitHub",
+ help_text="Ссылка на гитхаб автора",
+ )
+
+ email = models.EmailField(
+ verbose_name="Почта",
+ help_text="Почта автора",
+ )
+
+ class Meta:
+ verbose_name = "Данные об авторе"
+ verbose_name_plural = "Данные об авторе"
+
+ def __str__(self) -> str:
+ return f'Объект "Автор" (id={self.pk})'
diff --git a/src/author/templates/author/author_detail.html b/src/author/templates/author/author_detail.html
new file mode 100644
index 0000000..14737b2
--- /dev/null
+++ b/src/author/templates/author/author_detail.html
@@ -0,0 +1,11 @@
+{% extends 'base.html' %}
+
+{% load static %}
+
+{% block author_content %}
+
+{% endblock %}
\ No newline at end of file
diff --git a/src/author/templates/author/author_list.html b/src/author/templates/author/author_list.html
new file mode 100644
index 0000000..f1a803d
--- /dev/null
+++ b/src/author/templates/author/author_list.html
@@ -0,0 +1,13 @@
+{% extends 'base.html' %}
+
+{% load static %}
+
+{% block author_content %}
+ {% for author in object_list %}
+
+ {% endfor %}
+{% endblock %}
diff --git a/src/author/tests.py b/src/author/tests.py
new file mode 100644
index 0000000..a39e78b
--- /dev/null
+++ b/src/author/tests.py
@@ -0,0 +1,32 @@
+from django.test import TestCase
+
+from author.models import Author
+
+
+class JobTestCase(TestCase):
+ """
+ Тестирование функций работы
+ """
+
+ def setUp(self) -> None:
+ """
+ Настройка перед тестированием.
+ :return:
+ """
+
+ Author.objects.create(
+ resume_url="https://test.test",
+ github_url="https://test.test",
+ email="test@test.test",
+ )
+
+ def test_job_messages_creation(self) -> None:
+ """
+ Тестирование моделей работы.
+ :return:
+ """
+
+ author = Author.objects.get(resume_url="https://test.test")
+ self.assertEqual(author.github_url, "https://test.test")
+ self.assertEqual(author.email, "test@test.test")
+ self.assertEqual(str(author), f'Объект "Автор" (id={author.pk})')
diff --git a/src/author/urls.py b/src/author/urls.py
new file mode 100644
index 0000000..dbc2a52
--- /dev/null
+++ b/src/author/urls.py
@@ -0,0 +1,9 @@
+from django.urls import path
+
+from author.views import AutorListView
+from author.views import AutorDetailView
+
+urlpatterns = [
+ path("", AutorListView.as_view(), name="authors"),
+ path("/", AutorDetailView.as_view(), name="author"),
+]
diff --git a/src/author/views.py b/src/author/views.py
new file mode 100644
index 0000000..650808a
--- /dev/null
+++ b/src/author/views.py
@@ -0,0 +1,12 @@
+# Create your views here.
+from django.views.generic import DetailView, ListView
+
+from author.models import Author
+
+
+class AuthorListView(ListView):
+ model = Author
+
+
+class AuthorDetailView(DetailView):
+ model = Author
diff --git a/src/blog/migrations/0001_initial.py b/src/blog/migrations/0001_initial.py
index 3f830e8..d385322 100644
--- a/src/blog/migrations/0001_initial.py
+++ b/src/blog/migrations/0001_initial.py
@@ -1,6 +1,6 @@
-# Generated by Django 4.1.1 on 2022-09-20 15:45
-from typing import List
+# Generated by Django 4.1.7 on 2023-02-25 12:45
+import ckeditor_uploader.fields
from django.db import migrations, models
@@ -8,7 +8,7 @@ class Migration(migrations.Migration):
initial = True
- dependencies: List = []
+ dependencies = []
operations = [
migrations.CreateModel(
@@ -36,7 +36,12 @@ class Migration(migrations.Migration):
),
),
("title", models.CharField(max_length=255, verbose_name="Заголовок")),
- ("content", models.TextField(verbose_name="Содержимое сообщения")),
+ (
+ "content",
+ ckeditor_uploader.fields.RichTextUploadingField(
+ verbose_name="Содержимое сообщения"
+ ),
+ ),
(
"image",
models.ImageField(upload_to="images/", verbose_name="Изображение"),
@@ -47,7 +52,8 @@ class Migration(migrations.Migration):
),
],
options={
- "abstract": False,
+ "verbose_name": "Сообщение блога",
+ "verbose_name_plural": "Сообщения блога",
},
),
]
diff --git a/src/blog/migrations/0002_alter_blog_options_alter_blog_content.py b/src/blog/migrations/0002_alter_blog_options_alter_blog_content.py
deleted file mode 100644
index aab0535..0000000
--- a/src/blog/migrations/0002_alter_blog_options_alter_blog_content.py
+++ /dev/null
@@ -1,28 +0,0 @@
-# Generated by Django 4.1.1 on 2022-09-26 13:57
-
-import ckeditor_uploader.fields
-from django.db import migrations
-
-
-class Migration(migrations.Migration):
-
- dependencies = [
- ("blog", "0001_initial"),
- ]
-
- operations = [
- migrations.AlterModelOptions(
- name="blog",
- options={
- "verbose_name": "Сообщение блога",
- "verbose_name_plural": "Сообщения блога",
- },
- ),
- migrations.AlterField(
- model_name="blog",
- name="content",
- field=ckeditor_uploader.fields.RichTextUploadingField(
- verbose_name="Содержимое сообщения"
- ),
- ),
- ]
diff --git a/src/jobs/admin.py b/src/jobs/admin.py
index 2eec2ff..fb4e336 100644
--- a/src/jobs/admin.py
+++ b/src/jobs/admin.py
@@ -16,7 +16,7 @@ class JobAdmin(admin.ModelAdmin):
"updated_at",
)
- search_fields = ("description",)
+ search_fields = ("description", "content")
list_filter = (
"created_at",
diff --git a/src/jobs/migrations/0001_initial.py b/src/jobs/migrations/0001_initial.py
index 78f13cc..9820603 100644
--- a/src/jobs/migrations/0001_initial.py
+++ b/src/jobs/migrations/0001_initial.py
@@ -1,6 +1,6 @@
-# Generated by Django 4.1.1 on 2022-09-20 15:45
-from typing import List
+# Generated by Django 4.1.7 on 2023-02-25 12:45
+import ckeditor_uploader.fields
from django.db import migrations, models
@@ -8,7 +8,7 @@ class Migration(migrations.Migration):
initial = True
- dependencies: List = []
+ dependencies: list = []
operations = [
migrations.CreateModel(
@@ -51,6 +51,12 @@ class Migration(migrations.Migration):
verbose_name="Описание",
),
),
+ (
+ "content",
+ ckeditor_uploader.fields.RichTextUploadingField(
+ verbose_name="Подробное описание"
+ ),
+ ),
],
options={
"verbose_name": "Выполненная работа",
diff --git a/src/jobs/models.py b/src/jobs/models.py
index a5525d1..ee75694 100644
--- a/src/jobs/models.py
+++ b/src/jobs/models.py
@@ -1,7 +1,7 @@
"""
Модели для приложения "Jobs" (выполненные работы).
"""
-
+from ckeditor_uploader.fields import RichTextUploadingField
from django.db import models
from base.models import TimeStampMixin
@@ -22,6 +22,9 @@ class Job(TimeStampMixin):
verbose_name="Описание",
help_text="Краткое описание выполненной работы",
)
+ content = RichTextUploadingField(
+ verbose_name="Подробное описание",
+ )
class Meta:
verbose_name = "Выполненная работа"
@@ -29,3 +32,11 @@ class Meta:
def __str__(self) -> str:
return f'Объект "Выполненная работа" (id={self.pk})'
+
+ def summary(self) -> str:
+ """
+ Краткое содержание описания работы.
+
+ :return:
+ """
+ return self.content[:100] + "..."
diff --git a/src/jobs/templates/jobs/job_detail.html b/src/jobs/templates/jobs/job_detail.html
index 6b40e3f..a6b1911 100644
--- a/src/jobs/templates/jobs/job_detail.html
+++ b/src/jobs/templates/jobs/job_detail.html
@@ -3,11 +3,11 @@
{% block content %}
-
{{ blog.title }}
-
{{ blog.pub_date }}
-

-
{{ blog.content }}
-
Вернуться в блог
+
{{ job.title }}
+
{{ job.pub_date }}
+

+ {{ job.content|safe }}
+
На главную
{% endblock %}
diff --git a/src/jobs/templates/jobs/job_list.html b/src/jobs/templates/jobs/job_list.html
index 5765bc2..9b69ce6 100644
--- a/src/jobs/templates/jobs/job_list.html
+++ b/src/jobs/templates/jobs/job_list.html
@@ -34,9 +34,10 @@
{{ job.description }}
+
{{ job.summary|safe }}
{{ job.created_at|date:"d E Y" }}
diff --git a/src/jobs/tests.py b/src/jobs/tests.py
index e69de29..b24da08 100644
--- a/src/jobs/tests.py
+++ b/src/jobs/tests.py
@@ -0,0 +1,29 @@
+from django.test import TestCase
+
+from jobs.models import Job
+
+
+class JobTestCase(TestCase):
+ """
+ Тестирование функций работы
+ """
+
+ def setUp(self) -> None:
+ """
+ Настройка перед тестированием.
+ :return:
+ """
+
+ Job.objects.create(image="test", description="test", content="test" * 100)
+
+ def test_job_messages_creation(self) -> None:
+ """
+ Тестирование моделей работы.
+ :return:
+ """
+
+ job = Job.objects.get(description="test")
+
+ content = "test" * 100
+ self.assertEqual(job.summary(), content[:100] + "...")
+ self.assertEqual(str(job), f'Объект "Выполненная работа" (id={job.pk})')
diff --git a/src/jobs/urls.py b/src/jobs/urls.py
new file mode 100644
index 0000000..10af16e
--- /dev/null
+++ b/src/jobs/urls.py
@@ -0,0 +1,12 @@
+from django.urls import path
+
+from jobs.views import IndexJobsListView
+from jobs.views import JobDetailView
+
+urlpatterns = [
+ path("", IndexJobsListView.as_view(), name="jobs"),
+ path("
/", JobDetailView.as_view(), name="job"),
+ # path('/', views.DetailView.as_view(), name='detail'),
+ # path('/results/', views.ResultsView.as_view(), name='results'),
+ # path('/vote/', views.vote, name='vote'),
+]
diff --git a/src/jobs/views.py b/src/jobs/views.py
index 17fb444..a1f4330 100644
--- a/src/jobs/views.py
+++ b/src/jobs/views.py
@@ -1,7 +1,11 @@
-from django.views.generic import ListView
+from django.views.generic import ListView, DetailView
from jobs.models import Job
class IndexJobsListView(ListView):
model = Job
+
+
+class JobDetailView(DetailView):
+ model = Job
diff --git a/src/portfolio/migrations/__init__.py b/src/portfolio/migrations/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/src/portfolio/settings.py b/src/portfolio/settings.py
index dbe6dbd..94a6fb5 100644
--- a/src/portfolio/settings.py
+++ b/src/portfolio/settings.py
@@ -45,6 +45,7 @@
# созданные приложения
"jobs.apps.JobsConfig",
"blog.apps.BlogConfig",
+ "author.apps.AuthorConfig",
# установленные приложения
"ckeditor",
"ckeditor_uploader",
diff --git a/src/portfolio/templates/base.html b/src/portfolio/templates/base.html
index 42b2f67..2104ddb 100644
--- a/src/portfolio/templates/base.html
+++ b/src/portfolio/templates/base.html
@@ -35,11 +35,8 @@ Обо мне
Мои контакты
-
+ {% block author_content %}
+ {% endblock %}
diff --git a/src/portfolio/urls.py b/src/portfolio/urls.py
index 11ab691..398be18 100644
--- a/src/portfolio/urls.py
+++ b/src/portfolio/urls.py
@@ -22,12 +22,17 @@
from django.urls import path, include
from jobs.views import IndexJobsListView
+from author.views import AuthorListView
+
from portfolio import settings
+
urlpatterns = [
path("admin/", admin.site.urls),
path("ckeditor/", include("ckeditor_uploader.urls")),
- path("", IndexJobsListView.as_view(), name="home"),
+ path("", AuthorListView.as_view(), name="authors_content"),
+ path("", IndexJobsListView.as_view(), name="content"),
+ path("job/", include("jobs.urls")),
path("blog/", include("blog.urls")),
]
urlpatterns.extend(static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT))