Skip to content

Commit

Permalink
chore(fix): Refactored the server codebase with updated model schema
Browse files Browse the repository at this point in the history
  • Loading branch information
happychuks committed Aug 14, 2024
1 parent 636558c commit 4c88da1
Show file tree
Hide file tree
Showing 26 changed files with 396 additions and 164 deletions.
4 changes: 3 additions & 1 deletion client/src/components/ArticleDetail/ArticleDetail.js
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,9 @@ function ArticleDetail() {
/>
<CardContent>
<Typography variant="h4">{article.title}</Typography>
<Typography variant="h6">Author: {article.author}</Typography>
<Typography variant="h6">
Author(s): {article.authors.map(author => author.username).join(', ')}
</Typography>
<Typography variant="h6">
{formatDate(article.created_at)}
</Typography>
Expand Down
2 changes: 1 addition & 1 deletion client/src/components/ArticleList/ArticleList.js
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ function ArticleList() {
</Typography>
<Box mb={2} /> {/* Margin bottom for spacing */}
<Typography variant="body2" color="text.secondary">
Author: {article.author}
Author(s): {article.authors.map(author => author.username).join(', ')}
</Typography>
<Typography variant="body2" color="text.secondary">
{formatDate(article.created_at)}
Expand Down
82 changes: 8 additions & 74 deletions server/apps/research/admin.py
Original file line number Diff line number Diff line change
@@ -1,77 +1,11 @@
from django.contrib import admin
from django import forms
from django_ckeditor_5.widgets import CKEditor5Widget
from apps.research.models import Article, Category

# Import admin classes from separate files
from .admin.category_admin import CategoryAdmin
from .admin.author_admin import AuthorAdmin
from .admin.article_admin import ArticleAdmin

@admin.register(Category)
class CategoryAdmin(admin.ModelAdmin):
list_display = ('name', 'created_at')

list_per_page = 25

search_fields = ('name',)


# Override Django Admin config with CK Editor
@admin.register(Article)
class ArticleAdmin(admin.ModelAdmin):
# Order in which fields are displayed in the admin panel
fieldsets = [
(
'2077 Collective Articles', {
'fields': [
'title',
'category',
'thumb',
'content',
'summary',
'status',
],
}
),
]

def get_form(self, request, obj=None, **kwargs):
form = super().get_form(request, obj, **kwargs)
form.base_fields['content'].widget = CKEditor5Widget(attrs={"class": "django_ckeditor_5"})
return form

list_display = ('title', 'author', 'status', 'views', 'category', 'created_at')

search_fields = ('title', 'content', 'author__username')
# Number of articles per page
list_per_page = 25

# Add filters to the right side of the admin panel
list_filter = ('author', 'status', 'category', 'created_at')

# Make 'views' read-only
readonly_fields = ('views', 'author', 'slug')

# Make fields 'editable'
list_editable = ['status']

# Override save_model to automatically set author to the logged-in user when creating a new article
def save_model(self, request, obj, form, change):
if not change:
obj.author = request.user
super().save_model(request, obj, form, change)

# Override has_change_permission to restrict editing to the author only
def has_change_permission(self, request, obj=None):
has_class_permission = super().has_change_permission(request, obj)
if not has_class_permission:
return False
if obj is not None and obj.author != request.user:
return False
return True

# Override has_delete_permission to restrict deleting to the author only
def has_delete_permission(self, request, obj=None):
has_class_permission = super().has_delete_permission(request, obj)
if not has_class_permission:
return False
if obj is not None and obj.author != request.user:
return False
return True
# Register admin classes
admin.site.register(CategoryAdmin)
admin.site.register(AuthorAdmin)
admin.site.register(ArticleAdmin)
3 changes: 3 additions & 0 deletions server/apps/research/admin/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
from .category_admin import CategoryAdmin
from .author_admin import AuthorAdmin
from .article_admin import ArticleAdmin
59 changes: 59 additions & 0 deletions server/apps/research/admin/article_admin.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
from django.contrib import admin
from django import forms
from django_ckeditor_5.widgets import CKEditor5Widget
from apps.research.models import Article, Author

@admin.register(Article)
class ArticleAdmin(admin.ModelAdmin):
"""Admin interface for the Article model."""

fieldsets = [
('Article Details', {'fields': ['title', 'category', 'thumb', 'content', 'summary', 'status', 'authors']}),
]
list_display = ('title', 'display_authors', 'status', 'views', 'category', 'created_at')
search_fields = ('title', 'authors__user__username', 'authors__twitter_username', 'content')
list_per_page = 25
list_filter = ('authors', 'status', 'category', 'created_at')
readonly_fields = ('views', 'slug')
list_editable = ('status',)

def get_form(self, request, obj=None, **kwargs):
"""Return a form with CKEditor5Widget for the content field."""
form = super().get_form(request, obj, **kwargs)
if 'content' in form.base_fields:
form.base_fields['content'].widget = CKEditor5Widget(attrs={"class": "django_ckeditor_5"})
return form

def display_authors(self, obj):
"""Return a comma-separated list of authors for the article."""
return ", ".join(author.user.username for author in obj.authors.all())
display_authors.short_description = 'Authors'

def save_model(self, request, obj, form, change):
"""Automatically add the logged-in user as the author when creating a new article."""
if not change: # If creating a new article
author = Author.objects.filter(user=request.user).first() # Get the Author instance for the logged-in user
if author:
obj.authors.add(author)
super().save_model(request, obj, form, change)

def has_change_permission(self, request, obj=None):
"""Check if the user has permission to change the article."""

if request.user.is_superuser:
return True
if not super().has_change_permission(request, obj):
return False
if obj is not None and not obj.authors.filter(user=request.user).exists():
return False
return True

def has_delete_permission(self, request, obj=None):
"""Check if the user has permission to delete the article."""
if request.user.is_superuser:
return True
if not super().has_delete_permission(request, obj):
return False
if obj is not None and not obj.authors.filter(user=request.user).exists():
return False
return True
19 changes: 19 additions & 0 deletions server/apps/research/admin/author_admin.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
from django.contrib import admin
from apps.research.models import Author

@admin.register(Author)
class AuthorAdmin(admin.ModelAdmin):
"""Admin interface for the Author model."""

fieldsets = [
('Author Details', {'fields': ['user', 'bio', 'twitter_username']}),
]
list_display = ('user', 'bio', 'twitter_username')
list_per_page = 25
search_fields = ('user__username', 'twitter_username')

def save_model(self, request, obj, form, change):
"""Automatically set the author to the logged-in user when creating a new author."""
if not change: # If creating a new author
obj.user = request.user
super().save_model(request, obj, form, change)
10 changes: 10 additions & 0 deletions server/apps/research/admin/category_admin.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
from django.contrib import admin
from apps.research.models import Category

@admin.register(Category)
class CategoryAdmin(admin.ModelAdmin):
"""Admin interface for the Category model."""

list_display = ('name', 'created_at')
list_per_page = 25
search_fields = ('name',)
1 change: 1 addition & 0 deletions server/apps/research/managers.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,6 @@


class ArticleObjects(models.Manager):
"""Custom manager for the Article model."""
def get_queryset(self):
return super().get_queryset().filter(status='ready')
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
# Generated by Django 5.0.7 on 2024-08-14 01:46

import django.db.models.deletion
import uuid
from django.conf import settings
from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [
('research', '0002_initial'),
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
]

operations = [
migrations.RemoveField(
model_name='article',
name='author',
),
migrations.CreateModel(
name='Author',
fields=[
('id', models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False, unique=True)),
('created_at', models.DateTimeField(auto_now_add=True, db_index=True)),
('updated_at', models.DateTimeField(auto_now=True, null=True)),
('bio', models.TextField(blank=True)),
('twitter_username', models.CharField(blank=True, max_length=255)),
('user', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)),
],
options={
'verbose_name_plural': 'Author(s)',
},
),
migrations.AddField(
model_name='article',
name='authors',
field=models.ManyToManyField(blank=True, related_name='articles', to='research.author'),
),
]
17 changes: 17 additions & 0 deletions server/apps/research/migrations/0004_alter_author_options.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
# Generated by Django 5.0.7 on 2024-08-14 02:47

from django.db import migrations


class Migration(migrations.Migration):

dependencies = [
('research', '0003_remove_article_author_author_article_authors'),
]

operations = [
migrations.AlterModelOptions(
name='author',
options={'verbose_name_plural': 'Authors'},
),
]
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# Generated by Django 5.0.7 on 2024-08-14 06:45

from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [
('research', '0004_alter_author_options'),
]

operations = [
migrations.AlterField(
model_name='author',
name='bio',
field=models.TextField(blank=True, null=True),
),
migrations.AlterField(
model_name='author',
name='twitter_username',
field=models.CharField(blank=True, max_length=100, null=True),
),
]
59 changes: 5 additions & 54 deletions server/apps/research/models.py
Original file line number Diff line number Diff line change
@@ -1,56 +1,7 @@
from django.contrib.auth.models import User
from django.db import models
from django.utils.text import slugify
from django_ckeditor_5.fields import CKEditor5Field
# apps/research/models.py

from apps.common.models import BaseModel
from apps.research.managers import ArticleObjects
from .category import Category
from .author import Author
from .article import Article


class Category(BaseModel):
name = models.CharField(max_length=255)

class Meta:
verbose_name_plural = 'Categories'

def __str__(self):
return self.name


class Article(BaseModel):
options = (
('draft', 'Draft'),
('ready', 'Ready'),
)

title = models.CharField(max_length=100)
content = CKEditor5Field(null=True, blank=True, config_name='extends')
summary = models.TextField(blank=True)
author = models.ForeignKey(User, on_delete=models.SET_NULL, null=True, blank=True)
slug = models.SlugField(blank=True)
category = models.ForeignKey(Category, on_delete=models.SET_NULL, null=True, related_name="articles")
thumb = models.ImageField(upload_to='images/', default='../media/images/2077-Collective.png', blank=True)
views = models.PositiveBigIntegerField(default=0)
status = models.CharField(max_length=10, choices=options, default='draft')
objects = models.Manager()
post_objects = ArticleObjects()

class Meta:
ordering = ('-created_at',)

def __str__(self):
return self.title

def save(self, *args, **kwargs):
if not self.slug:
self.slug = self.generate_unique_slug()
super().save(*args, **kwargs)

def generate_unique_slug(self):
base_slug = slugify(self.title)
slug = base_slug
num = 1
while Article.objects.filter(slug=slug).exists():
slug = f"{base_slug}-{num}"
num += 1
return slug
__all__ = ['Category', 'Author', 'Article']
3 changes: 3 additions & 0 deletions server/apps/research/models/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
from .category import Category
from .author import Author
from .article import Article
Loading

0 comments on commit 4c88da1

Please sign in to comment.