First commit
This commit is contained in:
0
app/__init__.py
Normal file
0
app/__init__.py
Normal file
0
app/website/__init__.py
Normal file
0
app/website/__init__.py
Normal file
127
app/website/actions.py
Normal file
127
app/website/actions.py
Normal file
@ -0,0 +1,127 @@
|
||||
from .models import Post, Comment
|
||||
from .forms import SearchForm, CommentForm
|
||||
from asgiref.sync import async_to_sync
|
||||
from django.template.loader import render_to_string
|
||||
from django.urls import reverse
|
||||
|
||||
POST_PER_PAGE = 5
|
||||
|
||||
|
||||
def send_page(self, data={}):
|
||||
"""Render HTML and send page to client"""
|
||||
|
||||
# Prepare context data for page
|
||||
page = data["page"]
|
||||
context = {}
|
||||
data_reverse = {}
|
||||
match page:
|
||||
case "all posts":
|
||||
context = {
|
||||
"posts": Post.objects.all()[:POST_PER_PAGE],
|
||||
"form": SearchForm(),
|
||||
"next_page": 2,
|
||||
"is_last_page": (Post.objects.count() // POST_PER_PAGE) == 2,
|
||||
}
|
||||
case "single post":
|
||||
post = Post.objects.get(id=data["id"])
|
||||
context = {
|
||||
"post": post,
|
||||
"form": CommentForm(),
|
||||
"comments": Comment.objects.filter(post=post),
|
||||
}
|
||||
data_reverse = {"slug": post.slug}
|
||||
|
||||
# Render HTML nav and send to client
|
||||
context.update({"active_nav": page})
|
||||
self.send_html(
|
||||
{
|
||||
"selector": "#nav",
|
||||
"html": render_to_string("components/_nav.html", context),
|
||||
}
|
||||
)
|
||||
|
||||
# Render HTML page and send to client
|
||||
template_page = page.replace(" ", "_")
|
||||
self.send_html(
|
||||
{
|
||||
"selector": "#main",
|
||||
"html": render_to_string(f"pages/{template_page}.html", context),
|
||||
"url": reverse(page, kwargs=data_reverse),
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
def search(self, data={}):
|
||||
"""Search for posts"""
|
||||
# Prepare context data for page
|
||||
context = {
|
||||
"posts": Post.objects.filter(title__icontains=data["search"])[:POST_PER_PAGE]
|
||||
}
|
||||
|
||||
# Render HTML page and send to client
|
||||
self.send_html(
|
||||
{
|
||||
"selector": "#all-posts",
|
||||
"html": render_to_string("components/all_posts/list.html", context),
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
def add_next_posts(self, data={}):
|
||||
"""Add next posts from pagination"""
|
||||
# Prepare context data for page
|
||||
page = int(data["page"]) if "page" in data else 1
|
||||
start_of_slice = (page - 1) * POST_PER_PAGE
|
||||
end_of_slice = start_of_slice + POST_PER_PAGE
|
||||
context = {
|
||||
"posts": Post.objects.all()[start_of_slice:end_of_slice],
|
||||
"next_page": page + 1,
|
||||
"is_last_page": (Post.objects.count() // POST_PER_PAGE) == page,
|
||||
}
|
||||
|
||||
# Add and render HTML with new posts
|
||||
self.send_html(
|
||||
{
|
||||
"selector": "#all-posts",
|
||||
"html": render_to_string("components/all_posts/list.html", context),
|
||||
"append": True,
|
||||
}
|
||||
)
|
||||
|
||||
# Update paginator
|
||||
self.send_html(
|
||||
{
|
||||
"selector": "#paginator",
|
||||
"html": render_to_string(
|
||||
"components/all_posts/_button_paginator.html", context
|
||||
),
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
def add_comment(self, data):
|
||||
"""Add new comment to database"""
|
||||
# Add post
|
||||
data_with_post = data.copy()
|
||||
post = Post.objects.get(id=data["post_id"])
|
||||
data_with_post["post"] = post
|
||||
# Set initial values by CommentForm
|
||||
form = CommentForm(data_with_post)
|
||||
# Check if form is valid
|
||||
if form.is_valid():
|
||||
# Save comment
|
||||
form.save()
|
||||
# Render HTML with new comment to all clients
|
||||
async_to_sync(self.channel_layer.group_send)(
|
||||
self.room_name,
|
||||
{
|
||||
"type": "send.html", # Run "send_html()" method
|
||||
"selector": "#comments",
|
||||
"html": render_to_string(
|
||||
"components/_single_comment.html", {"comment": data}
|
||||
),
|
||||
"append": True,
|
||||
"broadcast": True,
|
||||
"url": reverse("single post", kwargs={"slug": post.slug}),
|
||||
},
|
||||
)
|
6
app/website/apps.py
Normal file
6
app/website/apps.py
Normal file
@ -0,0 +1,6 @@
|
||||
from django.apps import AppConfig
|
||||
|
||||
|
||||
class SimpleAppConfig(AppConfig):
|
||||
default_auto_field = "django.db.models.BigAutoField"
|
||||
name = "app.website"
|
50
app/website/consumers.py
Normal file
50
app/website/consumers.py
Normal file
@ -0,0 +1,50 @@
|
||||
# app/website/consumers.py
|
||||
from channels.generic.websocket import JsonWebsocketConsumer
|
||||
from asgiref.sync import async_to_sync
|
||||
import app.website.actions as actions
|
||||
|
||||
|
||||
class BlogConsumer(JsonWebsocketConsumer):
|
||||
room_name = "broadcast"
|
||||
|
||||
def connect(self):
|
||||
"""Event when client connects"""
|
||||
# Accept the connection
|
||||
self.accept()
|
||||
# Assign the Broadcast group
|
||||
async_to_sync(self.channel_layer.group_add)(self.room_name, self.channel_name)
|
||||
|
||||
def disconnect(self, close_code):
|
||||
"""Event when client disconnects"""
|
||||
pass
|
||||
|
||||
def receive_json(self, data_received):
|
||||
"""
|
||||
Event when data is received
|
||||
All information will arrive in 2 variables:
|
||||
"action", with the action to be taken
|
||||
"data" with the information
|
||||
"""
|
||||
# Get the data
|
||||
data = data_received["data"]
|
||||
# Depending on the action we will do one task or another.
|
||||
match data_received["action"]:
|
||||
case "Change page":
|
||||
actions.send_page(self, data)
|
||||
case "Search":
|
||||
actions.search(self, data)
|
||||
case "Add next posts":
|
||||
actions.add_next_posts(self, data)
|
||||
case "Add comment":
|
||||
actions.add_comment(self, data)
|
||||
|
||||
def send_html(self, event):
|
||||
"""Event: Send html to client"""
|
||||
data = {
|
||||
"selector": event["selector"],
|
||||
"html": event["html"],
|
||||
"append": "append" in event and event["append"],
|
||||
"broadcast": event["broadcast"] if "broadcast" in event else False,
|
||||
"url": event["url"] if "url" in event else "",
|
||||
}
|
||||
self.send_json(data)
|
21
app/website/feed.py
Normal file
21
app/website/feed.py
Normal file
@ -0,0 +1,21 @@
|
||||
from django.contrib.syndication.views import Feed
|
||||
from django.urls import reverse
|
||||
from .models import Post
|
||||
|
||||
|
||||
class LatestEntriesFeed(Feed):
|
||||
title = "My blog"
|
||||
link = "/feed/"
|
||||
description = "Updates to posts."
|
||||
|
||||
def items(self):
|
||||
return Post.objects.all()[:5]
|
||||
|
||||
def item_title(self, item):
|
||||
return item.title
|
||||
|
||||
def item_description(self, item):
|
||||
return item.summary
|
||||
|
||||
def item_link(self, item):
|
||||
return reverse("single post", kwargs={"slug": item.slug})
|
44
app/website/forms.py
Normal file
44
app/website/forms.py
Normal file
@ -0,0 +1,44 @@
|
||||
from django import forms
|
||||
from .models import Comment
|
||||
|
||||
|
||||
class SearchForm(forms.Form):
|
||||
search = forms.CharField(
|
||||
label="Search",
|
||||
max_length=255,
|
||||
required=False,
|
||||
widget=forms.TextInput(
|
||||
attrs={
|
||||
"id": "search",
|
||||
"class": "input",
|
||||
"placeholder": "Title...",
|
||||
}
|
||||
),
|
||||
)
|
||||
|
||||
|
||||
class CommentForm(forms.ModelForm):
|
||||
|
||||
author = forms.CharField(
|
||||
widget=forms.TextInput(
|
||||
attrs={
|
||||
"id": "author",
|
||||
"class": "input",
|
||||
"placeholder": "Your name...",
|
||||
}
|
||||
),
|
||||
)
|
||||
|
||||
content = forms.CharField(
|
||||
widget=forms.Textarea(
|
||||
attrs={
|
||||
"id": "content",
|
||||
"class": "input",
|
||||
"placeholder": "Your comment...",
|
||||
}
|
||||
),
|
||||
)
|
||||
|
||||
class Meta:
|
||||
model = Comment
|
||||
fields = ("author", "content", "post")
|
35
app/website/migrations/0001_initial.py
Normal file
35
app/website/migrations/0001_initial.py
Normal file
@ -0,0 +1,35 @@
|
||||
# Generated by Django 4.0 on 2022-04-29 06:43
|
||||
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
initial = True
|
||||
|
||||
dependencies = [
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='Post',
|
||||
fields=[
|
||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('title', models.CharField(max_length=200, unique=True)),
|
||||
('author', models.CharField(max_length=20)),
|
||||
('content', models.TextField()),
|
||||
('created_at', models.DateTimeField(auto_now_add=True)),
|
||||
],
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='Comment',
|
||||
fields=[
|
||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('name', models.CharField(max_length=20)),
|
||||
('content', models.TextField()),
|
||||
('created_at', models.DateTimeField(auto_now_add=True)),
|
||||
('post', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='website.post')),
|
||||
],
|
||||
),
|
||||
]
|
18
app/website/migrations/0002_rename_name_comment_author.py
Normal file
18
app/website/migrations/0002_rename_name_comment_author.py
Normal file
@ -0,0 +1,18 @@
|
||||
# Generated by Django 4.0 on 2022-04-29 06:57
|
||||
|
||||
from django.db import migrations
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('website', '0001_initial'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.RenameField(
|
||||
model_name='comment',
|
||||
old_name='name',
|
||||
new_name='author',
|
||||
),
|
||||
]
|
17
app/website/migrations/0003_alter_post_options.py
Normal file
17
app/website/migrations/0003_alter_post_options.py
Normal file
@ -0,0 +1,17 @@
|
||||
# Generated by Django 4.0 on 2022-05-15 15:58
|
||||
|
||||
from django.db import migrations
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('website', '0002_rename_name_comment_author'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterModelOptions(
|
||||
name='post',
|
||||
options={'ordering': ['-created_at']},
|
||||
),
|
||||
]
|
0
app/website/migrations/__init__.py
Normal file
0
app/website/migrations/__init__.py
Normal file
37
app/website/models.py
Normal file
37
app/website/models.py
Normal file
@ -0,0 +1,37 @@
|
||||
from django.db import models
|
||||
from django.utils.text import slugify
|
||||
from django.urls import reverse
|
||||
|
||||
|
||||
class Post(models.Model):
|
||||
title = models.CharField(max_length=200, unique=True)
|
||||
author = models.CharField(max_length=20)
|
||||
content = models.TextField()
|
||||
created_at = models.DateTimeField(auto_now_add=True)
|
||||
|
||||
class Meta:
|
||||
ordering = ["-created_at"]
|
||||
|
||||
@property
|
||||
def slug(self):
|
||||
return slugify(self.title)
|
||||
|
||||
@property
|
||||
def summary(self):
|
||||
return self.content[:100] + "..."
|
||||
|
||||
def get_absolute_url(self):
|
||||
return reverse("single post", kwargs={"slug": self.slug})
|
||||
|
||||
def __str__(self):
|
||||
return self.title
|
||||
|
||||
|
||||
class Comment(models.Model):
|
||||
author = models.CharField(max_length=20)
|
||||
content = models.TextField()
|
||||
post = models.ForeignKey(Post, on_delete=models.CASCADE)
|
||||
created_at = models.DateTimeField(auto_now_add=True)
|
||||
|
||||
def __str__(self):
|
||||
return self.name
|
23
app/website/templates/base.html
Normal file
23
app/website/templates/base.html
Normal file
@ -0,0 +1,23 @@
|
||||
{% load static %}
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
|
||||
<title>Example website</title>
|
||||
<link rel="stylesheet" href="{% static 'css/main.css' %}">
|
||||
<script defer src="{% static 'js/index.js' %}"></script>
|
||||
</head>
|
||||
<body
|
||||
data-host="{{ request.get_host }}"
|
||||
data-scheme="{{ request.scheme }}"
|
||||
>
|
||||
<div class="container">
|
||||
<header>
|
||||
<nav id="nav" class="nav">{% include 'components/_nav.html' %}</nav>
|
||||
</header>
|
||||
<main id="main">{% include page %}</main>
|
||||
<footer class="footer">My footer</footer>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
3
app/website/templates/components/_list_of_comments.html
Normal file
3
app/website/templates/components/_list_of_comments.html
Normal file
@ -0,0 +1,3 @@
|
||||
{% for comment in comments %}
|
||||
{% include "components/_single_comment.html" with comment=comment %}
|
||||
{% endfor %}
|
20
app/website/templates/components/_nav.html
Normal file
20
app/website/templates/components/_nav.html
Normal file
@ -0,0 +1,20 @@
|
||||
<ul class="nav__ul">
|
||||
<li>
|
||||
<a
|
||||
href="#"
|
||||
class="nav__link nav__link--page{% if active_nav == "all posts" %} active{% endif %}"
|
||||
data-target="all posts"
|
||||
>
|
||||
All posts
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a
|
||||
href="#"
|
||||
class="nav__link nav__link--page{% if active_nav == "about us" %} active{% endif %}"
|
||||
data-target="about us"
|
||||
>
|
||||
About us
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
5
app/website/templates/components/_single_comment.html
Normal file
5
app/website/templates/components/_single_comment.html
Normal file
@ -0,0 +1,5 @@
|
||||
<article>
|
||||
<h2>{{ comment.author }}</h2>
|
||||
<p>{{ comment.content }}</p>
|
||||
<p>{{ comment.created_at }}</p>
|
||||
</article>
|
@ -0,0 +1,3 @@
|
||||
{% if not is_last_page %}
|
||||
<button class="button" id="paginator" data-next-page="{{ next_page }}">More posts</button>
|
||||
{% endif %}
|
@ -0,0 +1,4 @@
|
||||
<form id="search-form" action="">
|
||||
{{ form.search }}
|
||||
<input class="button" type="submit" value="Search">
|
||||
</form>
|
14
app/website/templates/components/all_posts/list.html
Normal file
14
app/website/templates/components/all_posts/list.html
Normal file
@ -0,0 +1,14 @@
|
||||
{% for post in posts %}
|
||||
<article>
|
||||
<header>
|
||||
<h2>{{ post.title }}</h2>
|
||||
</header>
|
||||
<p>{{ post.summary }}</p>
|
||||
<p>{{ post.author }}</p>
|
||||
<footer>
|
||||
<p>
|
||||
<a class="post-item__link" href="#" data-page="single post" data-id="{{ post.id }}">Read more</a>
|
||||
</p>
|
||||
</footer>
|
||||
</article>
|
||||
{% endfor %}
|
1
app/website/templates/pages/404.html
Normal file
1
app/website/templates/pages/404.html
Normal file
@ -0,0 +1 @@
|
||||
<h1>404</h1>
|
34
app/website/templates/pages/about_us.html
Normal file
34
app/website/templates/pages/about_us.html
Normal file
@ -0,0 +1,34 @@
|
||||
<h1>About us</h1>
|
||||
<p>Lorem ipsum dolor sit amet, consectetur adipisicing elit. Ad animi aut beatae commodi consectetur cumque ipsam iste
|
||||
labore laudantium magni molestiae nobis nulla quod quos tempore totam velit, voluptas voluptates!</p>
|
||||
<p>Cumque distinctio dolor dolorem doloremque labore laborum libero magnam maiores maxime nam numquam obcaecati pariatur perferendis provident quae quia quis, quod, recusandae repudiandae rerum sed similique sunt vel voluptates voluptatum.</p>
|
||||
<p>Consequatur, consequuntur cum dignissimos distinctio doloremque ducimus odit temporibus veniam. Assumenda cumque, deserunt dicta ea eaque enim eveniet, incidunt inventore laboriosam magnam mollitia necessitatibus nobis nulla numquam odit vero voluptate!</p>
|
||||
<p>Accusantium aliquam architecto debitis deleniti dicta, eius enim exercitationem expedita facilis nulla numquam odio quam quis quo temporibus veritatis voluptas voluptate! Culpa cumque deserunt dolore id impedit itaque, maxime necessitatibus.</p>
|
||||
<p>Aspernatur dignissimos dolor enim error esse facere fugit, ipsa iure mollitia nemo optio perspiciatis placeat quae quaerat quisquam recusandae reiciendis reprehenderit sequi sint sit tempora vel veritatis vero voluptatibus voluptatum.</p>
|
||||
<p>Ab atque delectus deserunt dolorem dolores ducimus earum, esse eveniet exercitationem facilis illo in ipsam maxime nemo nesciunt nobis nulla quia quod quos rem sapiente soluta totam ullam unde vel?</p>
|
||||
<p>Aut autem dicta dolorem doloremque dolores eos eum expedita facere facilis illum, inventore laboriosam laudantium magnam mollitia nam non nostrum odit possimus quas, quibusdam quo repudiandae soluta totam velit voluptas.</p>
|
||||
<p>Eaque eos ipsum libero quod recusandae rerum saepe sunt veniam. Accusamus amet, aperiam aspernatur id odit quas quia tempora voluptatum. Autem doloribus ducimus ea esse fugit inventore, nihil quisquam ullam?</p>
|
||||
<p>Atque ducimus ea itaque odio quis quos recusandae ullam! Assumenda culpa, deserunt doloribus ipsa neque quo rerum temporibus veritatis voluptates. Alias aperiam dolorem impedit inventore maiores porro repellendus tempora vitae!</p>
|
||||
<p>Accusamus aliquam amet assumenda at dolorem doloribus ea eaque, earum illo illum in incidunt maiores nesciunt nulla odio quidem, ratione reiciendis sapiente sequi similique sint sunt velit veniam voluptatem voluptatibus?</p>
|
||||
<p>Debitis dolore fugiat ipsum mollitia odit officia provident, quisquam reprehenderit sed voluptates. Aut dicta ea est iure iusto quos tenetur totam! Aliquam culpa facilis ipsa modi, omnis quidem saepe similique.</p>
|
||||
<p>A animi aspernatur autem debitis dicta, dolores ea earum enim eos est fugit illo incidunt iste iure nam nostrum quisquam repellat! Doloremque, iure laboriosam. Beatae esse id iste nemo quod.</p>
|
||||
<p>Ab accusamus aut blanditiis consectetur et harum hic id ipsum labore nemo nihil nostrum nulla numquam porro quo quod rem repudiandae sapiente sed sit tempora, veniam vero, vitae voluptas voluptatum?</p>
|
||||
<p>Accusantium aliquam aliquid amet, asperiores aspernatur assumenda commodi dicta dolor ea eius esse inventore ipsam itaque natus nesciunt nobis nostrum obcaecati, odit optio recusandae rem sit soluta tempore unde velit.</p>
|
||||
<p>Ab accusamus accusantium aut cupiditate delectus deleniti et eveniet excepturi illo incidunt inventore ipsum labore quis similique ut, vel vitae? Beatae eum explicabo itaque iusto mollitia sed soluta velit voluptatibus?</p>
|
||||
<p>Beatae blanditiis consequatur debitis dicta distinctio dolor ducimus ea earum enim iste laborum magni maxime, odio, optio, quia quisquam quo suscipit tempora tempore vel. Labore minima similique unde. Ex, sint.</p>
|
||||
<p>Accusamus aspernatur assumenda cum distinctio ea earum fuga laboriosam necessitatibus odit pariatur quaerat recusandae, sint sit temporibus, tenetur veniam voluptas. At dolores magni repudiandae? Facilis illo magni pariatur rem repellendus.
|
||||
</p>
|
||||
<p>Accusantium animi autem dicta, ducimus eaque eum expedita fugiat fugit harum modi nam neque nesciunt nisi nulla odio, pariatur provident quidem sequi, sunt voluptatem. Animi consequuntur dolor impedit odio sequi?</p>
|
||||
<p>Asperiores corporis cum deleniti dolor dolorem est ex facere illo iusto maxime modi nisi repellendus rerum saepe, sequi totam ut voluptates! Distinctio excepturi, iste nesciunt odit perspiciatis porro quas quasi.</p>
|
||||
<p>A asperiores, cupiditate deserunt dolor doloribus ipsum minima mollitia nemo quis reiciendis! Illum itaque modi molestiae nisi numquam officia ratione, voluptatem. Corporis minima modi numquam odio rem sunt veniam, voluptas?</p>
|
||||
<p>Ad dolorum hic molestias odit officia placeat quas quibusdam, reiciendis voluptatibus. Explicabo, illum inventore molestiae odit recusandae repellat repellendus! Accusamus amet at dolore id, mollitia nihil optio sequi tempora unde.</p>
|
||||
<p> Eveniet excepturi illum nemo non sunt. Accusantium, consequatur, dolorem facere incidunt labore laboriosam neque, non omnis provident quod quos sed velit? Ab animi corporis ex exercitationem ipsam nam quae tenetur.</p>
|
||||
<p> Asperiores cumque ex fuga fugit similique, ut voluptate. Aliquid aspernatur at aut culpa explicabo fugit necessitatibus nemo nihil, non! Adipisci animi illum ipsa laboriosam nihil nobis reiciendis, repellendus unde ut.</p>
|
||||
<p>Aperiam assumenda aut beatae, cumque delectus dolores eius, facere laboriosam laudantium libero nam odio optio repellat suscipit veniam! A adipisci amet autem earum perspiciatis quae reprehenderit soluta unde vel voluptates?</p>
|
||||
<p>Accusamus adipisci alias asperiores commodi consectetur consequatur consequuntur dignissimos dolore earum et eum, ex hic id inventore laboriosam nesciunt officia officiis optio pariatur quisquam recusandae repudiandae saepe sequi totam ullam.</p>
|
||||
<p>Ab accusamus adipisci architecto asperiores blanditiis deleniti, dignissimos earum ex explicabo facilis fuga inventore iste iure laborum magnam obcaecati officia officiis perspiciatis provident quas repellat ullam voluptas voluptatem. Atque, pariatur.</p>
|
||||
<p>Animi at, cupiditate debitis dolores eaque excepturi illo impedit inventore ipsa libero magni minima natus nemo numquam officia possimus quam quis reiciendis sapiente sequi sunt suscipit velit veniam vitae voluptatem.</p>
|
||||
<p>Ab dolorum esse ipsam officia possimus repellat? Aliquid impedit nisi quae quas repellendus veniam. Molestiae quisquam, sapiente? Nesciunt quae reprehenderit similique soluta voluptatibus voluptatum! Adipisci dolor est iusto necessitatibus veniam?</p>
|
||||
<p> Commodi dolorum laudantium nemo omnis provident sunt ut! Distinctio, doloremque vitae? Alias aliquam deserunt dignissimos eaque et facere hic ipsam modi neque nisi numquam provident quas quisquam reiciendis sed, vitae.</p>
|
||||
<p>A ad blanditiis corporis eius, est facere in ipsa laudantium libero necessitatibus nisi rerum suscipit veniam! Doloremque, facere, possimus? Excepturi in minima modi nobis, non repudiandae sapiente unde. Aspernatur, molestiae.
|
||||
</p>
|
20
app/website/templates/pages/all_posts.html
Normal file
20
app/website/templates/pages/all_posts.html
Normal file
@ -0,0 +1,20 @@
|
||||
<h1>All posts</h1>
|
||||
<hr>
|
||||
{# Search #}
|
||||
<section id="form-search">
|
||||
{% include "components/all_posts/form_search.html" %}
|
||||
</section>
|
||||
{# End search #}
|
||||
<hr>
|
||||
<section>
|
||||
{# List posts #}
|
||||
<div id="all-posts">
|
||||
{% include "components/all_posts/list.html" %}
|
||||
</div>
|
||||
{# End list posts #}
|
||||
{# Paginator #}
|
||||
<div id="paginator">
|
||||
{% include "components/all_posts/_button_paginator.html" %}
|
||||
</div>
|
||||
{# End paginator #}
|
||||
</section>
|
27
app/website/templates/pages/single_post.html
Normal file
27
app/website/templates/pages/single_post.html
Normal file
@ -0,0 +1,27 @@
|
||||
<section>
|
||||
{# Post #}
|
||||
<article>
|
||||
<header>
|
||||
<h1>{{ post.title }}</h1>
|
||||
</header>
|
||||
<div>{{ post.content }}</div>
|
||||
<footer>
|
||||
<p>{{ post.author }}</p>
|
||||
</footer>
|
||||
</article>
|
||||
{# End post #}
|
||||
|
||||
{# Comments #}
|
||||
<div id="comments">
|
||||
<h2>Comments</h2>
|
||||
<form id="comment-form" action="" data-post-id="{{ post.id }}">
|
||||
{{ form.author }}
|
||||
{{ form.content }}
|
||||
<input class="button" type="submit" value="Add">
|
||||
</form>
|
||||
<div id="list-of-comments">
|
||||
{% include "components/_list_of_comments.html" %}
|
||||
</div>
|
||||
</div>
|
||||
{# End comments #}
|
||||
</section>
|
46
app/website/views.py
Normal file
46
app/website/views.py
Normal file
@ -0,0 +1,46 @@
|
||||
from django.shortcuts import render
|
||||
from .forms import SearchForm, CommentForm
|
||||
from .models import Post, Comment
|
||||
from .actions import POST_PER_PAGE
|
||||
|
||||
|
||||
def all_posts(request):
|
||||
return render(
|
||||
request,
|
||||
"base.html",
|
||||
{
|
||||
"posts": Post.objects.all()[:5],
|
||||
"page": "pages/all_posts.html",
|
||||
"active_nav": "all posts",
|
||||
"form": SearchForm(),
|
||||
"next_page": 2,
|
||||
"is_last_page": (Post.objects.count() // POST_PER_PAGE) == 2,
|
||||
},
|
||||
)
|
||||
|
||||
|
||||
def single_post(request, slug):
|
||||
post = list(filter(lambda post: post.slug == slug, Post.objects.all()))[0]
|
||||
return render(
|
||||
request,
|
||||
"base.html",
|
||||
{
|
||||
"post": post,
|
||||
"page": "pages/single_post.html",
|
||||
"active_nav": "single post",
|
||||
"comments": Comment.objects.filter(post=post),
|
||||
"form": CommentForm(),
|
||||
},
|
||||
)
|
||||
|
||||
|
||||
def about(request):
|
||||
return render(
|
||||
request,
|
||||
"base.html",
|
||||
{"page": "pages/about_us.html", "active_nav": "about us"},
|
||||
)
|
||||
|
||||
|
||||
def page_not_found(request, exception):
|
||||
return render(request, "base.html", {"page": "pages/404.html"})
|
Reference in New Issue
Block a user