Add basis to work on articles
This commit is contained in:
parent
ec31d838c1
commit
75e1bec1d0
13 changed files with 324 additions and 4 deletions
|
@ -1,6 +1,37 @@
|
|||
from django.contrib import admin
|
||||
from django.contrib.admin import register
|
||||
from django.contrib.auth.admin import UserAdmin
|
||||
|
||||
from .models import User
|
||||
from .models import Article, User
|
||||
|
||||
admin.site.register(User, UserAdmin)
|
||||
|
||||
|
||||
@register(Article)
|
||||
class ArticleAdmin(admin.ModelAdmin):
|
||||
list_display = [
|
||||
"title",
|
||||
"status",
|
||||
"author",
|
||||
"created_at",
|
||||
"published_at",
|
||||
"updated_at",
|
||||
]
|
||||
list_display_links = ["title"]
|
||||
list_filter = ["status"]
|
||||
date_hierarchy = "created_at"
|
||||
fieldsets = [
|
||||
(
|
||||
"Metadata",
|
||||
{
|
||||
"fields": (
|
||||
"title",
|
||||
"status",
|
||||
("created_at", "published_at", "updated_at"),
|
||||
"author",
|
||||
)
|
||||
},
|
||||
),
|
||||
("Content", {"fields": ("content",)}),
|
||||
]
|
||||
readonly_fields = ["created_at", "updated_at"]
|
||||
|
|
130
articles/migrations/0001_initial.py
Normal file
130
articles/migrations/0001_initial.py
Normal file
|
@ -0,0 +1,130 @@
|
|||
# Generated by Django 3.1 on 2020-08-14 12:56
|
||||
|
||||
import django.contrib.auth.models
|
||||
import django.contrib.auth.validators
|
||||
import django.utils.timezone
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
initial = True
|
||||
|
||||
dependencies = [
|
||||
("auth", "0012_alter_user_first_name_max_length"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name="User",
|
||||
fields=[
|
||||
(
|
||||
"id",
|
||||
models.AutoField(
|
||||
auto_created=True,
|
||||
primary_key=True,
|
||||
serialize=False,
|
||||
verbose_name="ID",
|
||||
),
|
||||
),
|
||||
("password", models.CharField(max_length=128, verbose_name="password")),
|
||||
(
|
||||
"last_login",
|
||||
models.DateTimeField(
|
||||
blank=True, null=True, verbose_name="last login"
|
||||
),
|
||||
),
|
||||
(
|
||||
"is_superuser",
|
||||
models.BooleanField(
|
||||
default=False,
|
||||
help_text="Designates that this user has all permissions without explicitly assigning them.",
|
||||
verbose_name="superuser status",
|
||||
),
|
||||
),
|
||||
(
|
||||
"username",
|
||||
models.CharField(
|
||||
error_messages={
|
||||
"unique": "A user with that username already exists."
|
||||
},
|
||||
help_text="Required. 150 characters or fewer. Letters, digits and @/./+/-/_ only.",
|
||||
max_length=150,
|
||||
unique=True,
|
||||
validators=[
|
||||
django.contrib.auth.validators.UnicodeUsernameValidator()
|
||||
],
|
||||
verbose_name="username",
|
||||
),
|
||||
),
|
||||
(
|
||||
"first_name",
|
||||
models.CharField(
|
||||
blank=True, max_length=150, verbose_name="first name"
|
||||
),
|
||||
),
|
||||
(
|
||||
"last_name",
|
||||
models.CharField(
|
||||
blank=True, max_length=150, verbose_name="last name"
|
||||
),
|
||||
),
|
||||
(
|
||||
"email",
|
||||
models.EmailField(
|
||||
blank=True, max_length=254, verbose_name="email address"
|
||||
),
|
||||
),
|
||||
(
|
||||
"is_staff",
|
||||
models.BooleanField(
|
||||
default=False,
|
||||
help_text="Designates whether the user can log into this admin site.",
|
||||
verbose_name="staff status",
|
||||
),
|
||||
),
|
||||
(
|
||||
"is_active",
|
||||
models.BooleanField(
|
||||
default=True,
|
||||
help_text="Designates whether this user should be treated as active. Unselect this instead of deleting accounts.",
|
||||
verbose_name="active",
|
||||
),
|
||||
),
|
||||
(
|
||||
"date_joined",
|
||||
models.DateTimeField(
|
||||
default=django.utils.timezone.now, verbose_name="date joined"
|
||||
),
|
||||
),
|
||||
(
|
||||
"groups",
|
||||
models.ManyToManyField(
|
||||
blank=True,
|
||||
help_text="The groups this user belongs to. A user will get all permissions granted to each of their groups.",
|
||||
related_name="user_set",
|
||||
related_query_name="user",
|
||||
to="auth.Group",
|
||||
verbose_name="groups",
|
||||
),
|
||||
),
|
||||
(
|
||||
"user_permissions",
|
||||
models.ManyToManyField(
|
||||
blank=True,
|
||||
help_text="Specific permissions for this user.",
|
||||
related_name="user_set",
|
||||
related_query_name="user",
|
||||
to="auth.Permission",
|
||||
verbose_name="user permissions",
|
||||
),
|
||||
),
|
||||
],
|
||||
options={
|
||||
"verbose_name": "user",
|
||||
"verbose_name_plural": "users",
|
||||
"abstract": False,
|
||||
},
|
||||
managers=[("objects", django.contrib.auth.models.UserManager()),],
|
||||
),
|
||||
]
|
50
articles/migrations/0002_article.py
Normal file
50
articles/migrations/0002_article.py
Normal file
|
@ -0,0 +1,50 @@
|
|||
# Generated by Django 3.1 on 2020-08-14 13:16
|
||||
|
||||
import django.db.models.deletion
|
||||
from django.conf import settings
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
("articles", "0001_initial"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name="Article",
|
||||
fields=[
|
||||
(
|
||||
"id",
|
||||
models.AutoField(
|
||||
auto_created=True,
|
||||
primary_key=True,
|
||||
serialize=False,
|
||||
verbose_name="ID",
|
||||
),
|
||||
),
|
||||
("title", models.CharField(max_length=255)),
|
||||
("content", models.TextField()),
|
||||
(
|
||||
"status",
|
||||
models.CharField(
|
||||
choices=[("draft", "Draft"), ("published", "Published")],
|
||||
default="draft",
|
||||
max_length=15,
|
||||
),
|
||||
),
|
||||
("published_at", models.DateTimeField(null=True)),
|
||||
("created_at", models.DateTimeField(auto_now_add=True)),
|
||||
("updated_at", models.DateTimeField(auto_now=True)),
|
||||
(
|
||||
"author",
|
||||
models.ForeignKey(
|
||||
on_delete=django.db.models.deletion.PROTECT,
|
||||
to=settings.AUTH_USER_MODEL,
|
||||
),
|
||||
),
|
||||
],
|
||||
options={"ordering": ["-published_at"],},
|
||||
),
|
||||
]
|
18
articles/migrations/0003_auto_20200814_1522.py
Normal file
18
articles/migrations/0003_auto_20200814_1522.py
Normal file
|
@ -0,0 +1,18 @@
|
|||
# Generated by Django 3.1 on 2020-08-14 13:22
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
("articles", "0002_article"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name="article",
|
||||
name="published_at",
|
||||
field=models.DateTimeField(blank=True, null=True),
|
||||
),
|
||||
]
|
|
@ -1,6 +1,35 @@
|
|||
import markdown
|
||||
from django.contrib.auth.models import AbstractUser
|
||||
from django.db import models
|
||||
|
||||
|
||||
class User(AbstractUser):
|
||||
pass
|
||||
|
||||
|
||||
class Article(models.Model):
|
||||
DRAFT = "draft"
|
||||
PUBLISHED = "published"
|
||||
STATUS_CHOICES = [
|
||||
(DRAFT, "Draft"),
|
||||
(PUBLISHED, "Published"),
|
||||
]
|
||||
title = models.CharField(max_length=255)
|
||||
content = models.TextField()
|
||||
status = models.CharField(max_length=15, choices=STATUS_CHOICES, default=DRAFT)
|
||||
published_at = models.DateTimeField(null=True, blank=True)
|
||||
created_at = models.DateTimeField(auto_now_add=True)
|
||||
updated_at = models.DateTimeField(auto_now=True)
|
||||
author = models.ForeignKey(User, on_delete=models.PROTECT)
|
||||
|
||||
class Meta:
|
||||
ordering = ["-published_at"]
|
||||
|
||||
def get_abstract(self):
|
||||
md = markdown.Markdown(extensions=["extra"])
|
||||
html = md.convert(self.content)
|
||||
return html.split("<!--more-->")[0]
|
||||
|
||||
def get_formatted_content(self):
|
||||
md = markdown.Markdown(extensions=["extra"])
|
||||
return md.convert(self.content)
|
||||
|
|
6
articles/static/style.css
Normal file
6
articles/static/style.css
Normal file
|
@ -0,0 +1,6 @@
|
|||
.content {
|
||||
max-width: 640px;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
font-family: Arial, sans-serif;
|
||||
}
|
12
articles/templates/articles/article_detail.html
Normal file
12
articles/templates/articles/article_detail.html
Normal file
|
@ -0,0 +1,12 @@
|
|||
{% extends 'articles/base.html' %}
|
||||
|
||||
{% block content %}
|
||||
<h1>{{ article.title }}</h1>
|
||||
<p>By: {{ article.author }}</p>
|
||||
<p>Published at: {{ article.published_at }}</p>
|
||||
<p>Updated at: {{ article.updated_at }}</p>
|
||||
|
||||
<div>
|
||||
{{ article.get_formatted_content|safe }}
|
||||
</div>
|
||||
{% endblock %}
|
12
articles/templates/articles/article_list.html
Normal file
12
articles/templates/articles/article_list.html
Normal file
|
@ -0,0 +1,12 @@
|
|||
{% extends 'articles/base.html' %}
|
||||
|
||||
{% block content %}
|
||||
<h1>Articles list</h1>
|
||||
{% for article in articles %}
|
||||
<article>
|
||||
<h2>{{ article.title }}</h2>
|
||||
<p>{{ article.get_abstract|safe }}</p>
|
||||
<p><a href="{% url 'article-detail' pk=article.pk %}">Read more</a></p>
|
||||
</article>
|
||||
{% endfor %}
|
||||
{% endblock %}
|
15
articles/templates/articles/base.html
Normal file
15
articles/templates/articles/base.html
Normal file
|
@ -0,0 +1,15 @@
|
|||
{% load static %}
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>Gabnotes</title>
|
||||
<link rel="stylesheet" href="{% static 'style.css' %}" type="text/css">
|
||||
</head>
|
||||
<body>
|
||||
<div class="content">
|
||||
{% block content %}
|
||||
{% endblock %}
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
|
@ -1,3 +1,14 @@
|
|||
from django.shortcuts import render
|
||||
from django.views import generic
|
||||
|
||||
# Create your views here.
|
||||
from articles.models import Article
|
||||
|
||||
|
||||
class ArticlesListView(generic.ListView):
|
||||
model = Article
|
||||
paginate_by = 15
|
||||
context_object_name = "articles"
|
||||
|
||||
|
||||
class ArticleDetailView(generic.DetailView):
|
||||
model = Article
|
||||
context_object_name = "article"
|
||||
|
|
|
@ -37,6 +37,7 @@ INSTALLED_APPS = [
|
|||
"django.contrib.sessions",
|
||||
"django.contrib.messages",
|
||||
"django.contrib.staticfiles",
|
||||
"articles",
|
||||
]
|
||||
|
||||
MIDDLEWARE = [
|
||||
|
@ -99,7 +100,7 @@ AUTH_PASSWORD_VALIDATORS = [
|
|||
|
||||
LANGUAGE_CODE = "en-us"
|
||||
|
||||
TIME_ZONE = "UTC"
|
||||
TIME_ZONE = "Europe/Paris"
|
||||
|
||||
USE_I18N = True
|
||||
|
||||
|
|
|
@ -16,6 +16,10 @@ Including another URLconf
|
|||
from django.contrib import admin
|
||||
from django.urls import path
|
||||
|
||||
from articles import views
|
||||
|
||||
urlpatterns = [
|
||||
path("admin/", admin.site.urls),
|
||||
path("", views.ArticlesListView.as_view(), name="articles-list"),
|
||||
path("<int:pk>", views.ArticleDetailView.as_view(), name="article-detail"),
|
||||
]
|
||||
|
|
|
@ -1,2 +1,3 @@
|
|||
django==3.1
|
||||
pre-commit==2.6.0
|
||||
markdown==3.2.2
|
||||
|
|
Reference in a new issue