Skip to content

Commit

Permalink
feat(articles): tag articles (#33)
Browse files Browse the repository at this point in the history
chore(serializers): uncomment code

chore(serializers): remove duplicated class

chore(migrations): make new migrations

chore(tagging): rename tagList to tags

chore(serializers): add docstring

chore(migrations): delete migrations

feat(migrations): update migrations
  • Loading branch information
SnyderMbishai authored and Mnickii committed Dec 19, 2018
1 parent 67e5f0a commit f28e4ad
Show file tree
Hide file tree
Showing 9 changed files with 147 additions and 26 deletions.
20 changes: 20 additions & 0 deletions authors/apps/articles/migrations/0004_article_tags.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# Generated by Django 2.1 on 2018-12-19 11:00

from django.db import migrations
import taggit.managers


class Migration(migrations.Migration):

dependencies = [
('taggit', '0002_auto_20150616_2121'),
('articles', '0003_auto_20181218_2105'),
]

operations = [
migrations.AddField(
model_name='article',
name='tags',
field=taggit.managers.TaggableManager(blank=True, help_text='A comma-separated list of tags.', through='taggit.TaggedItem', to='taggit.Tag', verbose_name='Tags'),
),
]
3 changes: 3 additions & 0 deletions authors/apps/articles/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

from django.db import models
from django.utils.text import slugify
from taggit.managers import TaggableManager

from authors.apps.profiles.models import Profile
from ..authentication.models import User
Expand All @@ -15,6 +16,8 @@ class Article(models.Model):
description = models.TextField(blank=False)
body = models.TextField()
image = models.URLField(blank=True)
# Article tags
tags = TaggableManager(blank=True)
# slug is unique to an article
article_slug = models.SlugField(unique=True, editable=False, max_length=255)
author = models.ForeignKey(User, related_name='authorshaven', on_delete=models.CASCADE)
Expand Down
67 changes: 47 additions & 20 deletions authors/apps/articles/serializers.py
Original file line number Diff line number Diff line change
@@ -1,35 +1,61 @@
from rest_framework import serializers
from rest_framework.exceptions import ParseError

from authors.apps.profiles.serializers import ProfileSerializer
from .models import Article, Rating, ReportArticle, Comment, Reply, BookMarkArticle
from ..authentication.serializers import UserSerializer
from ..authentication.models import User
from rest_framework.validators import UniqueTogetherValidator

class ArticleTagSerializer(serializers.Field):
"""
class for handling article tags serialization.
"""
def to_internal_value(self, data):
if type(data) is not list:
raise ParseError("Expect 'tags' to be a list." )
return data

def to_representation(self, obj):
if type(obj) is not list:
return [tag for tag in obj.all()]
return obj


class ArticleSerializer(serializers.ModelSerializer):
"""
Class to serialize article details.
"""
tags = ArticleTagSerializer(default=[])
title = serializers.CharField(max_length=200)
description = serializers.CharField()
body = serializers.CharField()
author = serializers.HiddenField(
default=serializers.CurrentUserDefault()
)

class Meta:
"""
Method defines what fields of an article object should be displayed.
"""
model = Article
fields = '__all__'
fields = ("title", "description", "body", "author", "tags")

def create(self, data):
def validate_tagList(self, validated_data):
if type(validated_data) is not list:
raise serializers.ValidationError("not valid")
return validated_data

def create(self, data, *args):
"""
Method enables the creation of an article.
"""
return Article.objects.create(**data)
new_article = Article(**data)
new_article.save()
article = Article.objects.get(pk=new_article.pk)
for tag in new_article.tags:
article.tags.add(tag)
return new_article

def update(self, instance, data):
"""
Expand All @@ -39,7 +65,12 @@ def update(self, instance, data):
instance.description = data.get('description', instance.description)
instance.body = data.get('body', instance.body)
instance.author_id = data.get('authors_id', instance.author_id)
instance.save()
if 'tagList' not in data:
return instance
instance.tagList = data.get('tagList')
article = Article.objects.get(pk=instance.pk)
article.tagList.set(*instance.tagList, clear=True)
# instance.save()
return instance

def get_author(self, Article):
Expand All @@ -58,7 +89,16 @@ class ArticleAuthorSerializer(serializers.ModelSerializer):
title = serializers.CharField(max_length=200)
description = serializers.CharField()
body = serializers.CharField()
author = UserSerializer(read_only=True)
author = UserSerializer(read_only = True)
tags = serializers.SerializerMethodField(method_name='show_tags')


def show_tags(self, instance):
"""
Show tag details.
"""
return instance.tags.names()


def likes(self, instance):
"""method to return a user who has liked an article"""
Expand Down Expand Up @@ -91,6 +131,7 @@ def favorite(self, instance):

class Meta:
model = Article
# fields = ('title', 'description', 'body', 'like', 'dislike', 'favorite','author')
fields = '__all__'


Expand Down Expand Up @@ -193,20 +234,6 @@ class Meta:
fields = ('rating',)


class ArticleAuthorSerializer(serializers.ModelSerializer):
"""
Class to serialize article and return the full owner information.
"""
title = serializers.CharField(max_length=200)
description = serializers.CharField()
body = serializers.CharField()
author = UserSerializer(read_only=True)

class Meta:
model = Article
fields = '__all__'


class ReportArticleSerializer(serializers.ModelSerializer):
article = serializers.PrimaryKeyRelatedField(queryset=Article.objects.all())
reported_by = serializers.PrimaryKeyRelatedField(queryset=User.objects.all())
Expand Down
6 changes: 4 additions & 2 deletions authors/apps/articles/tests/base_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -64,14 +64,16 @@ def setUp(self):
"article": {
"title": "Hello world",
"description": "Ever wonder how?",
"body": "You have to believe"
"body": "You have to believe",
"tags": ["react", "django", "redux"]
}
}
self.article_update_data = {
"article": {
"title": "Hello HelloWorld",
"description": "Ever wonder how?",
"body": "You have to believe"
"body": "You have to believe",
"tags": ["react", "django", "redux"]
}
}
self.article_invalid_data = {
Expand Down
4 changes: 3 additions & 1 deletion authors/apps/articles/tests/test_ratings.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
import unittest
import json

from django.urls import reverse
from rest_framework.test import (APIClient,
APITestCase)
from rest_framework import status


@unittest.skip("Skip this class")
@unittest.skip("Not implemented")
class TestRatings(APITestCase):
def setUp(self):
self.token = self.login().get("token", "")
Expand Down
62 changes: 62 additions & 0 deletions authors/apps/articles/tests/test_tagging.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
from rest_framework import status
from .base_test import BaseTestCase

class TestArticleTagging(BaseTestCase):
"""
Test class for article tagging.
"""
def test_successful_tagging_article(self):
"""
Test method for successful article tagging.
"""
saved_article = self.create_article()[1]
self.assertEqual(saved_article.status_code, status.HTTP_201_CREATED)
self.assertEqual(saved_article.data['title'], self.article_data['article']['title'])

def test_tagging_without_authentication_fails(self):
"""
Test for tagging without authentication.
"""
self.user_signup()
response = self.test_client.post(self.articles_url, self.article_invalid_data, format='json')
self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN)

def test_empty_tag_list_field_is_allowed(self):
"""
Test that a blank tag list is allowed.
"""
data = {
"article": {
"title": "Hello world",
"description": "Ever wonder how?",
"body": "You have to believe",
"tagList": []
}
}
self.user_signup()
token = 'Token ' + self.user_login()
response = self.test_client.post(self.articles_url,
data, format='json',
HTTP_AUTHORIZATION=token)

self.assertEqual(response.status_code, status.HTTP_201_CREATED)

def test_non_list_tagList_data_fails(self):
"""
Test that non list type of data for tag is not allowed.
"""
data = {
"article": {
"title": "Hello world",
"description": "Ever wonder how?",
"body": "You have to believe",
"tags": "hello, world"
}
}
self.user_signup()
token = 'Token ' + self.user_login()
response = self.test_client.post(self.articles_url,
data, format='json',
HTTP_AUTHORIZATION=token)
self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)

9 changes: 6 additions & 3 deletions authors/apps/articles/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,18 +40,21 @@ def post(self, request):
saved_article = serializer.save()
message = {'message': "The article '{}' has been successfully created.".format(saved_article.title),
'title': saved_article.title,
'slug': saved_article.article_slug}
'slug': saved_article.article_slug,
'tags':saved_article.tags
}
return Response(message, status=status.HTTP_201_CREATED)

def get(self, request):
"""
Method for getting all articles.
"""
# It gets a specific article using the slug that is provided in the url
# It gets a specific all articles
articles = Article.objects.all()

if articles:
result = []
for article in articles:
for article in articles:
individual = dict()
individual.update(ArticleAuthorSerializer(article).data)
individual.update({"rating": article.just_average})
Expand Down
1 change: 1 addition & 0 deletions authors/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@
'authors.apps.profiles',
'rest_framework_swagger',
'social_django',
'taggit',
]

MIDDLEWARE = [
Expand Down
1 change: 1 addition & 0 deletions requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ django-extensions==2.1.4
django-oauth-toolkit==1.2.0
django-rest-framework-social-oauth2==1.1.0
django-rest-swagger==2.1.0
django-taggit==0.23.0
djangorestframework==3.9.0
djangorestframework-jwt==1.11.0
gunicorn==19.9.0
Expand Down

0 comments on commit f28e4ad

Please sign in to comment.