добавил краткое описание и форматирование в заголовок, описание и текст статьи
parent
23e8a2e492
commit
7c6fc02529
|
@ -1,8 +1,8 @@
|
|||
"""initial_migration
|
||||
|
||||
Revision ID: af05a1cbd975
|
||||
Revision ID: 8c415e462cb3
|
||||
Revises:
|
||||
Create Date: 2023-09-19 22:57:11.510132
|
||||
Create Date: 2023-09-22 12:19:22.923813
|
||||
|
||||
"""
|
||||
import flask_security
|
||||
|
@ -10,7 +10,7 @@ import sqlalchemy as sa
|
|||
from alembic import op
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = "af05a1cbd975"
|
||||
revision = "8c415e462cb3"
|
||||
down_revision = None
|
||||
branch_labels = None
|
||||
depends_on = None
|
||||
|
@ -20,8 +20,8 @@ def upgrade():
|
|||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.create_table(
|
||||
"role",
|
||||
sa.Column("id", sa.Integer(), nullable=False),
|
||||
sa.Column("name", sa.String(length=80), nullable=False),
|
||||
sa.Column("id", sa.Integer(), nullable=False),
|
||||
sa.Column("description", sa.String(length=255), nullable=True),
|
||||
sa.Column("permissions", flask_security.datastore.AsaList(), nullable=True),
|
||||
sa.Column(
|
||||
|
@ -78,8 +78,9 @@ def upgrade():
|
|||
"post",
|
||||
sa.Column("id", sa.Integer(), autoincrement=True, nullable=False),
|
||||
sa.Column("author", sa.Integer(), nullable=True),
|
||||
sa.Column("slug", sa.String(length=30), nullable=False),
|
||||
sa.Column("title", sa.String(length=50), nullable=False),
|
||||
sa.Column("slug", sa.String(length=100), nullable=False),
|
||||
sa.Column("title", sa.Text(), nullable=False),
|
||||
sa.Column("description", sa.Text(), nullable=False),
|
||||
sa.Column("published", sa.Boolean(), nullable=True),
|
||||
sa.Column("create_datetime", sa.DateTime(), nullable=True),
|
||||
sa.Column("update_datetime", sa.DateTime(), nullable=True),
|
|
@ -1,4 +1,4 @@
|
|||
from flask import abort, redirect, request, url_for
|
||||
from flask import abort, g, redirect, request, url_for
|
||||
from flask_admin.contrib import sqla
|
||||
from flask_ckeditor import CKEditorField
|
||||
from flask_security import current_user
|
||||
|
@ -72,6 +72,7 @@ class PostView(MyAdminView):
|
|||
tags="Тэги",
|
||||
slug="Слаг",
|
||||
title="Заголовок",
|
||||
description="Краткое описание статьи",
|
||||
author="Автор",
|
||||
published="Опубликовано",
|
||||
create_datetime="Дата создания",
|
||||
|
@ -79,6 +80,10 @@ class PostView(MyAdminView):
|
|||
text="Текст",
|
||||
)
|
||||
|
||||
form_overrides = dict(text=CKEditorField)
|
||||
form_overrides = dict(
|
||||
title=CKEditorField,
|
||||
description=CKEditorField,
|
||||
text=CKEditorField,
|
||||
)
|
||||
create_template = "admin/edit.html"
|
||||
edit_template = "admin/edit.html"
|
||||
|
|
|
@ -76,6 +76,10 @@ def create_app(test_config=None):
|
|||
|
||||
app.register_blueprint(bp_cli)
|
||||
|
||||
from pyproger.blog.blog import bp as bp_blog
|
||||
|
||||
app.register_blueprint(bp_blog)
|
||||
|
||||
@security.context_processor
|
||||
def security_context_processor():
|
||||
return dict(
|
||||
|
|
|
@ -0,0 +1,9 @@
|
|||
from flask import Blueprint
|
||||
|
||||
bp = Blueprint(
|
||||
"bp_blog",
|
||||
__name__,
|
||||
template_folder="templates/blog",
|
||||
)
|
||||
|
||||
from . import urls
|
|
@ -0,0 +1,43 @@
|
|||
import locale
|
||||
|
||||
from flask import render_template, render_template_string, request
|
||||
|
||||
from ..dbase.database import get_paginated_posts, get_post
|
||||
from .blog import bp
|
||||
|
||||
locale.setlocale(locale.LC_ALL, "")
|
||||
|
||||
|
||||
@bp.route("/", methods=["GET"], defaults={"page": 1})
|
||||
@bp.route("/<int:page>", methods=["GET"])
|
||||
def index(page=1):
|
||||
per_page = 3
|
||||
posts, total_pages = get_paginated_posts(page, per_page)
|
||||
list_pages = [
|
||||
x for x in range(1, total_pages + 1) if x >= page - 2 and x <= page + 2
|
||||
]
|
||||
return render_template(
|
||||
"blog/index.html",
|
||||
request=request,
|
||||
posts=posts,
|
||||
title="pyproger - разговоры про питон",
|
||||
menu_title="pyproger",
|
||||
page=page,
|
||||
total_pages=total_pages,
|
||||
list_pages=list_pages,
|
||||
)
|
||||
|
||||
|
||||
@bp.route("/post/")
|
||||
@bp.route("/post/<path:slug>")
|
||||
def post(slug=None):
|
||||
if slug:
|
||||
current_post = get_post(slug)
|
||||
return render_template(
|
||||
"blog/postview.html",
|
||||
title=f"pyproger - {current_post.Post.title}",
|
||||
menu_title="pyproger",
|
||||
post=current_post,
|
||||
)
|
||||
else:
|
||||
return render_template_string("noup")
|
|
@ -0,0 +1,24 @@
|
|||
from . import db
|
||||
from .models import Post, User
|
||||
|
||||
|
||||
def get_paginated_posts(page, per_page):
|
||||
quer = (
|
||||
db.session.query(Post, User)
|
||||
.join(User, Post.author == User.id)
|
||||
.order_by(Post.create_datetime.desc())
|
||||
.paginate(page=page, per_page=per_page, error_out=False)
|
||||
)
|
||||
total_pages = quer.total // per_page + [0, 1][quer.total % per_page != 0]
|
||||
|
||||
return quer, total_pages
|
||||
|
||||
|
||||
def get_post(slug):
|
||||
post_query = (
|
||||
db.session.query(Post, User)
|
||||
.join(User, Post.author == User.id)
|
||||
.filter(Post.slug == slug)
|
||||
.first()
|
||||
)
|
||||
return post_query
|
|
@ -1,5 +1,6 @@
|
|||
import datetime
|
||||
|
||||
from flask_security import current_user
|
||||
from flask_security.models import fsqla
|
||||
from sqlalchemy import Boolean, Column, DateTime, Integer, String, Text
|
||||
|
||||
|
@ -69,8 +70,9 @@ class Post(db.Model):
|
|||
autoincrement=True,
|
||||
)
|
||||
author = Column(Integer, db.ForeignKey("user.id"))
|
||||
slug = Column(String(30), nullable=False)
|
||||
title = Column(String(50), nullable=False)
|
||||
slug = Column(String(100), nullable=False)
|
||||
title = Column(Text, nullable=False)
|
||||
description = Column(Text, nullable=False)
|
||||
published = Column(Boolean, default=False)
|
||||
tags = db.relationship("Tag", secondary=tag_post)
|
||||
|
||||
|
@ -82,5 +84,6 @@ class Post(db.Model):
|
|||
update_datetime = Column(
|
||||
DateTime(),
|
||||
nullable=True,
|
||||
onupdate=datetime.datetime.utcnow(),
|
||||
)
|
||||
text = Column(Text)
|
||||
|
|
|
@ -8,5 +8,7 @@ if you have set the configuration variables more than CKEDITOR_SERVE_LOCAL and C
|
|||
or you need to config the CKEditor textarea, use the line below to register the configuration.
|
||||
The name value should be the name of the CKEditor form field.
|
||||
#}
|
||||
{{ ckeditor.config(name='Title') }}
|
||||
|
||||
{{ ckeditor.config(name='Text') }}
|
||||
{% endblock %}
|
||||
|
|
|
@ -0,0 +1,50 @@
|
|||
<!doctype html>
|
||||
<html lang="{{page_lang}}">
|
||||
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>
|
||||
{% block title %}
|
||||
{% endblock title %}
|
||||
</title>
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet"
|
||||
integrity="sha384-9ndCyUaIbzAi2FUVXJi0CjmCapSmO7SnpJef0486qhLnuZ2cdeRhO02iuK6FUUVM" crossorigin="anonymous">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div id="main-container" class="container-fluid">
|
||||
|
||||
<nav class="navbar p-2 navbar-expand-lg bg-dark border-bottom border-bottom-dark rounded-bottom-4"
|
||||
data-bs-theme="dark">
|
||||
<div class="container-fluid">
|
||||
<a class="navbar-brand" href="{{ url_for(".index")}}">
|
||||
{% block menu_title %}
|
||||
|
||||
{% endblock menu_title %}</a>
|
||||
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarSupportedContent"
|
||||
aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Переключатель навигации">
|
||||
<span class="navbar-toggler-icon"></span>
|
||||
</button>
|
||||
<div class="collapse navbar-collapse" id="navbarSupportedContent">
|
||||
<ul class="navbar-nav me-auto mb-2 mb-lg-0">
|
||||
text
|
||||
</ul>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
<div id="content-container" class="container-fluid">
|
||||
{% block content %} {% endblock %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"
|
||||
integrity="sha384-geWF76RCwLtnZ8qwWowPQNguL3RmwHVBC9FhGdlKrxdiJJigb/j/68SIy3Te4Bkz"
|
||||
crossorigin="anonymous"></script>
|
||||
</body>
|
||||
|
||||
|
||||
</html>
|
|
@ -0,0 +1,58 @@
|
|||
{% extends 'blog/base.html' %}
|
||||
|
||||
{% block title %}
|
||||
{{title}}
|
||||
{% endblock %}
|
||||
|
||||
{% block menu_title %}
|
||||
{{ menu_title }}
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="conteiner">
|
||||
<br>
|
||||
</div>
|
||||
|
||||
<div class="container px-4 px-lg-5">
|
||||
<div class="list-group justify-content-center">
|
||||
|
||||
{% for p in posts %}
|
||||
<a href="{{ url_for(".post", slug=p.Post.slug )}}" class="list-group-item list-group-item-action border-0">
|
||||
<h5 class="post-title">{{ p.Post.title | safe }}</h5>
|
||||
{{ p.Post.description | safe}}
|
||||
</a>
|
||||
<small>
|
||||
Опубликовал
|
||||
<a href="mailto:{{p.User.email}}">
|
||||
{{ p.User.username}}
|
||||
</a>
|
||||
{{ p.Post.create_datetime.strftime('%d %B, %Y') }}
|
||||
</small>
|
||||
<hr class="my-4" />
|
||||
{% endfor %}
|
||||
</div>
|
||||
|
||||
<nav class="container">
|
||||
<ul class="pagination justify-content-start"
|
||||
{% if posts.has_prev %}
|
||||
<li class="page-item"><a class="page-link" href="{{ url_for(".index", page=posts.prev_num)}}"><<</a></li>
|
||||
{% else %}
|
||||
<li class="page-item disabled"><a class="page-link" ><<</a></li>
|
||||
{% endif %}
|
||||
|
||||
{% for i in list_pages %}
|
||||
{% if i == page %}
|
||||
<li class="page-item active"><a class="page-link" href="{{ url_for(".index", page=i)}}">{{ i }}</a></li>
|
||||
{% else %}
|
||||
<li class="page-item"><a class="page-link" href="{{ url_for(".index", page=i)}}">{{ i }}</a></li>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
|
||||
{% if posts.has_next %}
|
||||
<li class="page-item"><a class="page-link" href="{{ url_for(".index", page=posts.next_num)}}">>></a></li>
|
||||
{% else %}
|
||||
<li class="page-item disabled"><a class="page-link" >>></a></li>
|
||||
{% endif %}
|
||||
|
||||
</nav>
|
||||
{% endblock %}
|
|
@ -0,0 +1,19 @@
|
|||
{% extends 'blog/base.html' %}
|
||||
|
||||
{% block title %}
|
||||
{{title}}
|
||||
{% endblock %}
|
||||
|
||||
{% block menu_title %}
|
||||
{{ menu_title }}
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<!-- Main Content-->
|
||||
<div class="conteiner">
|
||||
<br>
|
||||
<h4>{{ post.Post.title | safe }}</h4>
|
||||
{{ post.Post.text | safe }}
|
||||
</div>
|
||||
|
||||
{% endblock %}
|
|
@ -0,0 +1,9 @@
|
|||
<html>
|
||||
|
||||
<body>
|
||||
<div>
|
||||
<a href="{{ url_for('admin.index') }}">админ панель</a>
|
||||
</div>
|
||||
</body>
|
||||
|
||||
</html>
|
Loading…
Reference in New Issue