{{ article.title }}
+{{ article.get_abstract|safe }}
+ +diff --git a/articles/admin.py b/articles/admin.py index 6d53b53..78cfcd3 100644 --- a/articles/admin.py +++ b/articles/admin.py @@ -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"] diff --git a/articles/migrations/0001_initial.py b/articles/migrations/0001_initial.py new file mode 100644 index 0000000..8880233 --- /dev/null +++ b/articles/migrations/0001_initial.py @@ -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()),], + ), + ] diff --git a/articles/migrations/0002_article.py b/articles/migrations/0002_article.py new file mode 100644 index 0000000..2029669 --- /dev/null +++ b/articles/migrations/0002_article.py @@ -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"],}, + ), + ] diff --git a/articles/migrations/0003_auto_20200814_1522.py b/articles/migrations/0003_auto_20200814_1522.py new file mode 100644 index 0000000..601011b --- /dev/null +++ b/articles/migrations/0003_auto_20200814_1522.py @@ -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), + ), + ] diff --git a/articles/models.py b/articles/models.py index 7e21811..4f55131 100644 --- a/articles/models.py +++ b/articles/models.py @@ -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("")[0] + + def get_formatted_content(self): + md = markdown.Markdown(extensions=["extra"]) + return md.convert(self.content) diff --git a/articles/static/style.css b/articles/static/style.css new file mode 100644 index 0000000..4c9cc2f --- /dev/null +++ b/articles/static/style.css @@ -0,0 +1,6 @@ +.content { + max-width: 640px; + margin-left: auto; + margin-right: auto; + font-family: Arial, sans-serif; +} diff --git a/articles/templates/articles/article_detail.html b/articles/templates/articles/article_detail.html new file mode 100644 index 0000000..7e45337 --- /dev/null +++ b/articles/templates/articles/article_detail.html @@ -0,0 +1,12 @@ +{% extends 'articles/base.html' %} + +{% block content %} +
By: {{ article.author }}
+Published at: {{ article.published_at }}
+Updated at: {{ article.updated_at }}
+ +{{ article.get_abstract|safe }}
+ +