Skip to content

Commit

Permalink
Merge 9cf00cc into d87f346
Browse files Browse the repository at this point in the history
  • Loading branch information
kelvinwm committed Jan 30, 2019
2 parents d87f346 + 9cf00cc commit 5e22146
Show file tree
Hide file tree
Showing 14 changed files with 243 additions and 69 deletions.
25 changes: 23 additions & 2 deletions authors/apps/articles/migrations/0001_initial.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# Generated by Django 2.1.4 on 2019-01-28 09:25
# Generated by Django 2.1.4 on 2019-01-30 06:35

from django.conf import settings
from django.db import migrations, models
Expand All @@ -10,8 +10,8 @@ class Migration(migrations.Migration):
initial = True

dependencies = [
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
('contenttypes', '0002_remove_content_type_name'),
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
]

operations = [
Expand All @@ -27,10 +27,31 @@ class Migration(migrations.Migration):
('updated_at', models.DateField(auto_now=True)),
('author', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='author', to=settings.AUTH_USER_MODEL)),
],
),
migrations.CreateModel(
name='Comments',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('created_at', models.DateTimeField(auto_now_add=True)),
('updated_at', models.DateField(auto_now=True)),
('body', models.TextField(max_length=200)),
('is_Child', models.BooleanField(default=False)),
('article', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='comments', to='articles.Article')),
('author', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='author_rel', to=settings.AUTH_USER_MODEL)),
('parent', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='threads', to='articles.Comments')),
],
options={
'ordering': ['-created_at'],
},
),
migrations.CreateModel(
name='FavoriteArticle',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('article', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='articles.Article')),
('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)),
],
),
migrations.CreateModel(
name='LikeDislike',
fields=[
Expand Down
48 changes: 0 additions & 48 deletions authors/apps/articles/migrations/0002_auto_20190128_1814.py

This file was deleted.

16 changes: 15 additions & 1 deletion authors/apps/articles/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,14 @@
GenericForeignKey
from django.contrib.contenttypes.models import ContentType
from django.db import models
from django.db.models.signals import pre_save

from .utils import get_unique_slug
from django.db.models.signals import pre_save

# local imports
from ..authentication.models import User

"""
Articles
"""
Expand Down Expand Up @@ -73,7 +76,8 @@ class Comments(models.Model):
article = models.ForeignKey(Article, related_name='comments',
on_delete=models.CASCADE, null=False)
parent = models.ForeignKey('self', null=True, blank=True,
on_delete=models.CASCADE, related_name='threads')
on_delete=models.CASCADE,
related_name='threads')

def __str__(self):
return str(self.body)
Expand All @@ -88,3 +92,13 @@ def slug_pre_save_receiver(sender, instance, *args, **kwargs):


pre_save.connect(slug_pre_save_receiver, sender=Article)


class FavoriteArticle(models.Model):
"""Favorite Article model"""

article = models.ForeignKey(Article, on_delete=models.CASCADE)
user = models.ForeignKey(User, on_delete=models.CASCADE)

def __str__(self):
return "{}".format(self.article)
8 changes: 6 additions & 2 deletions authors/apps/articles/renderers.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,13 @@ class CommentJSONRenderer(JSONRenderer):
charset = 'utf-8'

def render(self, data, media_type=None, renderer_context=None):
if isinstance(data, ReturnList):
return json.dumps({
'comments': data,
'commentsCount': len(data)
})
return json.dumps({
'comment': data,
'commentsCount': len(data)
'comment': data
})


Expand Down
6 changes: 5 additions & 1 deletion authors/apps/articles/response_messages.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,13 @@
"article_404": "Article not found",
"null_update": "The request body cannot be null",
"authentication": "Authentication credentials were not provided",
"error_favoring": "You cannot report your own article."
}

success_messages = {
"updated": "article updated successfully",
"deleted": "Article has been deleted"
"deleted": "Article has been deleted",
"remove_favorite":
"You have successfully removed this article from favorites",
"add_favorite": "Article successfully added to favorites",
}
31 changes: 26 additions & 5 deletions authors/apps/articles/serializers.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
from rest_framework import serializers

from .models import Article, Comments
from .models import Article, Comments, LikeDislike
from authors.apps.profiles.models import UserProfile
from .models import Article, LikeDislike
from authors.apps.articles.models import FavoriteArticle
from authors.apps.profiles.serializers import ProfileSerialiazer


Expand Down Expand Up @@ -46,7 +45,6 @@ def validate(self, data):


class UpdateArticleSerializer(serializers.ModelSerializer):

author = serializers.SerializerMethodField()
body = serializers.CharField(required=True)
title = serializers.CharField(required=True)
Expand Down Expand Up @@ -120,10 +118,33 @@ class Meta:
'author',
'article',
'parent',
'is_Child',
)


class LikeDislikeSerializer(serializers.ModelSerializer):

class Meta:
model = LikeDislike


class FavouriteSerializer(serializers.ModelSerializer):
author = serializers.SerializerMethodField()
title = serializers.SerializerMethodField()
slug = serializers.SerializerMethodField()

def get_author(self, favorite):
author = favorite.article.author.username
return author

def get_title(self, favorite):
title = favorite.article.title
return title

def get_slug(self, favorite):
slug = favorite.article.slug
return slug

class Meta:
model = FavoriteArticle

fields = ('title', 'slug', 'author', 'article', 'user')
3 changes: 3 additions & 0 deletions authors/apps/articles/tests.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
from django.test import TestCase

# Create your tests here.
17 changes: 12 additions & 5 deletions authors/apps/articles/urls.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
from django.urls import path

from authors.apps.articles.views.comments import(
CommentsListView,
CommentsRetrieveUpdateDestroy
from authors.apps.articles.models import LikeDislike
from authors.apps.articles.views.comments import (
CommentsListView, CommentsRetrieveUpdateDestroy
)
from authors.apps.articles.views.articles import (GetUpdateDeleteArticle,
CreateArticleView,
LikeDislikeArticleView)
from authors.apps.articles.models import Article, LikeDislike
from authors.apps.articles.views.favorite import FavouriteArticle, \
GetFavouriteArticles

"""
Django 2.0 requires the app_name variable set when using include namespace
Expand All @@ -19,15 +20,21 @@
name='article_create'),
path('/<slug:slug>', GetUpdateDeleteArticle.as_view(),
name='detail_article'),

path('/<slug>/comments', CommentsListView.as_view(), name='comment'),

path('/<slug>/comments/<int:id>',
CommentsRetrieveUpdateDestroy.as_view(), name='thread'
),
path('', CreateArticleView.as_view(), name='article-create'),
path('/<slug>/like',
LikeDislikeArticleView.as_view(vote_type=LikeDislike.LIKE),
name='article-like-like'),
path('/<slug>/dislike',
LikeDislikeArticleView.as_view(vote_type=LikeDislike.DISLIKE),
name='article-like-dislike'),

path('/<slug>/favorite', FavouriteArticle.as_view(),
name='favorite_article'),
path('/favorite/', GetFavouriteArticles.as_view(),
name='all_favourite_article'),
]
1 change: 0 additions & 1 deletion authors/apps/articles/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@


def get_unique_slug(model_instance, slugable_field_name, slug_field_name):

slug = slugify(getattr(model_instance, slugable_field_name))
unique_slug = slug
extension = ''.join(random.choices(
Expand Down
5 changes: 2 additions & 3 deletions authors/apps/articles/views/comments.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
from authors.apps.articles.renderers import (
ArticleJSONRenderer, CommentJSONRenderer
)
from authors.apps.articles.views import articles


# Create your views here.
class CommentsListView(ListCreateAPIView):
Expand All @@ -43,7 +43,6 @@ def create(self, request, slug, *args, **kwargs):
return Response(serializer.data, status=status.HTTP_201_CREATED)

def get(self, request, slug, *args, **kwargs):

article = Article.objects.filter(slug=slug).first()
if not article:
message = {"error": "Article doesn't exist"}
Expand Down Expand Up @@ -81,7 +80,7 @@ def create(self, request, slug, id):
'body': body,
'parent': parent,
'article': article.pk,
'is_Child':True
'is_Child': True
}

serializer = self.serializer_class(
Expand Down
68 changes: 68 additions & 0 deletions authors/apps/articles/views/favorite.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
# local imports
from rest_framework import status
from rest_framework.generics import (ListCreateAPIView,
RetrieveUpdateDestroyAPIView)
from rest_framework.permissions import IsAuthenticated
from rest_framework.response import Response
from rest_framework.views import APIView

from authors.apps.articles.models import Article, FavoriteArticle
from authors.apps.articles.renderers import ArticleJSONRenderer
from authors.apps.articles.response_messages import (success_messages,
error_messages)

from authors.apps.articles.serializers import (FavouriteSerializer,
ArticleSerializer)


# Create your views here.
class CreateArticleView(ListCreateAPIView):
permission_classes = (IsAuthenticated,)
serializer_class = ArticleSerializer
renderer_classes = (ArticleJSONRenderer,)
queryset = Article.objects.all()

def perform_create(self, serializer):
serializer.save(author=self.request.user)


class FavouriteArticle(RetrieveUpdateDestroyAPIView):
permission_classes = (IsAuthenticated,)
serializer_class = FavouriteSerializer

def put(self, request, *args, **kwargs):

article = Article.objects.filter(slug=kwargs['slug']).first()
if not article:
msg = {"message": error_messages['article_404']}
return Response(msg, status.HTTP_404_NOT_FOUND)

favorite_article = FavoriteArticle.objects.filter(
user=request.user.id, article=article.id).exists()

# if article is already a favorite, remove from favorites
if favorite_article:
instance = FavoriteArticle.objects.filter(
user=request.user.id, article=article.id)

self.perform_destroy(instance)
msg = {"message": success_messages['remove_favorite']}
return Response(msg, status.HTTP_200_OK)

# if article is not a favorite, then make it favorite
data = {"article": article.id, "user": request.user.id}
serializer = self.serializer_class(data=data)
serializer.is_valid(raise_exception=True)
serializer.save()
msg = {"message": success_messages['add_favorite']}
return Response(msg, status.HTTP_200_OK)


class GetFavouriteArticles(APIView):
permission_classes = (IsAuthenticated,)
serializer_class = FavouriteSerializer

def get(self, request):
fav_article = FavoriteArticle.objects.filter(user=request.user.id)
serializer = FavouriteSerializer(fav_article, many=True)
return Response(serializer.data, status.HTTP_200_OK)
Loading

0 comments on commit 5e22146

Please sign in to comment.