Skip to content

Commit

Permalink
Merge 4fc5f23 into 4576e38
Browse files Browse the repository at this point in the history
  • Loading branch information
KabohaJeanMark committed Apr 30, 2019
2 parents 4576e38 + 4fc5f23 commit 1f65633
Show file tree
Hide file tree
Showing 23 changed files with 610 additions and 2 deletions.
5 changes: 5 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -95,3 +95,8 @@ ENV/

# SQLite3
db.sqlite3

#the coverage file
.coveragerc

htmlcov/*
Empty file.
3 changes: 3 additions & 0 deletions authors/apps/articles/admin.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
from django.contrib import admin

# Register your models here.
5 changes: 5 additions & 0 deletions authors/apps/articles/apps.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
from django.apps import AppConfig


class ArticlesConfig(AppConfig):
name = 'articles'
17 changes: 17 additions & 0 deletions authors/apps/articles/fixtures/article.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
[
{
"model": "articles.article",
"pk": 1,
"fields": {
"slug" : "this-is-andela-ftub",
"title": "this is andela",
"description": "The best tech startapp operating",
"body": "The tech startapp harder to get into than Harvard",
"created_at": "2019-04-29T14:13:25.526681Z",
"updated_at": "2019-04-29T14:13:25.526692Z",
"favorited": false,
"favorite_count": 0,
"author_id": "jeanmarcus"
}
}
]
25 changes: 25 additions & 0 deletions authors/apps/articles/fixtures/user.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
[
{
"model": "authentication.user",
"pk": 1,
"fields": {
"username": "jeanmarcus",
"email": "jean@gmail.com",
"email_verified": true,
"password": "pbkdf2_sha256$120000$RLkaJYAyp0Dq$+2V+D1qXP5P1UuEjLxaRAorMiw2khCxKevg+/YrCAyI=",
"created_at": "2019-04-29T14:13:25.526681Z",
"updated_at": "2019-04-29T14:13:25.526692Z"
}
},
{
"model": "authentication.user",
"pk": 2,
"fields": {
"username": "jean",
"email": "jean2@gmail.com",
"password": "pbkdf2_sha256$120000$RLkaJYAyp0Dq$+2V+D1qXP5P1UuEjLxaRAorMiw2khCxKevg+/YrCAyI=",
"created_at": "2019-04-29T14:13:25.526681Z",
"updated_at": "2019-04-29T14:13:25.526692Z"
}
}
]
33 changes: 33 additions & 0 deletions authors/apps/articles/migrations/0001_initial.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
# Generated by Django 2.1 on 2019-04-25 19:53

from django.conf import settings
from django.db import migrations, models
import django.db.models.deletion
import django.utils.timezone


class Migration(migrations.Migration):

initial = True

dependencies = [
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
]

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=120)),
('slug', models.SlugField(blank=True, null=True)),
('description', models.TextField()),
('body', models.TextField()),
('created_at', models.DateTimeField(default=django.utils.timezone.now)),
('updated_at', models.DateTimeField(default=django.utils.timezone.now)),
('favorited', models.BooleanField(default=False)),
('favorite_count', models.IntegerField(default=0)),
('author', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL, to_field='username')),
],
),
]
Empty file.
39 changes: 39 additions & 0 deletions authors/apps/articles/models.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
from django.db import models
from django.db.models.signals import pre_save
from django.utils import timezone

from authors.apps.authentication.models import User
from .utils import unique_slug_generator


class Article(models.Model):
"""
Model class for creating an article
"""

title = models.CharField(max_length=120)
slug = models.SlugField(blank=True,
null=True)
description = models.TextField()
body = models.TextField()
created_at = models.DateTimeField(default=timezone.now)
updated_at = models.DateTimeField(default=timezone.now)
favorited = models.BooleanField(default=False)
favorite_count = models.IntegerField(default=0)
author = models.ForeignKey(
User, to_field='username', on_delete=models.CASCADE, null=False)

def __str__(self):
"""
return string representation of the article
model class
"""
return self.title


def slug_generator(sender, instance, *args, **kwargs):
if not instance.slug:
instance.slug = unique_slug_generator(instance)


pre_save.connect(slug_generator, sender=Article)
8 changes: 8 additions & 0 deletions authors/apps/articles/permissions.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
from rest_framework.permissions import BasePermission, SAFE_METHODS


class IsOwnerOrReadOnly(BasePermission):
def has_object_permission(self, request, view, obj):
if request.method in SAFE_METHODS:
return True
return obj.author == request.user
30 changes: 30 additions & 0 deletions authors/apps/articles/renderer.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import json

from rest_framework.renderers import JSONRenderer


class ArticleJSONRenderer(JSONRenderer):
charset = 'utf-8'

def render(self, data, media_type=None, renderer_context=None):
# If the view throws an error (such as the article can't be created
# or something similar due to missing fields), `data` will contain an `errors` key. We want
# the default JSONRenderer to handle rendering errors, so we need to
# check for this case.
errors =''
try:
errors = data.get('errors', None)
except:
pass


if errors:
# As mentioned about, we will let the default JSONRenderer handle
# rendering errors.
return super(ArticleJSONRenderer, self).render(data)


# Finally, we can render our data under the "user" namespace.
return json.dumps({
'article': data
})
53 changes: 53 additions & 0 deletions authors/apps/articles/serializers.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
from rest_framework import serializers

from .models import Article
from .validations import ValidateArticleCreation


class ArticleSerializer(serializers.ModelSerializer):

title = serializers.CharField(
required=True,
error_messages={
"required": "The title field is required",
"blank": "The title field cannot be left blank"
}
)
description = serializers.CharField(
required=True,
error_messages={
"required": "The description field is required",
"blank": "The description field cannot be left blank"
}
)
body = serializers.CharField(
required=True,
error_messages={
"required": "The body field is required",
"blank": "The body field cannot be left blank"
}
)

def validate(self, data):
title = data.get('title', None)
description = data.get('description', None)
body = data.get('body', None)

validator = ValidateArticleCreation()
validator.validate_title(title)
validator.validate_title(description)
validator.validate_title(body)

return {
'title': title,
'body': body,
'description': description
}

class Meta:
"""class for returning our field."""

model = Article
fields = '__all__'
read_only_fields = ('created_at', 'updated_at', 'author',
'favorited', 'favorite_count', 'slug')
Empty file.
86 changes: 86 additions & 0 deletions authors/apps/articles/tests/base.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
from rest_framework.test import APIClient, APITestCase
from django.urls import reverse
from authors.apps.authentication.models import User


class BaseTestCase(APITestCase):
"""
Base Test class for out tests in this app
Class will also house the setup and teardown
methods for our tests
"""

def setUp(self):
# Initialize the Testclient for the tests
self.client = APIClient()


self.login_url = reverse('authentication:user_login')

self.user = User.objects.create_user(
username="test1", email="test1@gmail.com", password="password")
setattr(self.user, 'email_verified', True)
self.user.save()

self.login_data = {
"user": {
"email": "test1@gmail.com",
"password": "password"
}
}

self.admin_login_response = self.client.post(
self.login_url, self.login_data, format='json')
admin_test_token = self.admin_login_response.data['token']
self.auth_header = 'Bearer {}'.format(admin_test_token)

self.create_article_data = {
"title": "Fresh kid wonders on stage at lugogo",
"description": "he wows the kids",
"body": "Fresh kid is a 5 year musician who has been on map.",
"author": self.user.username
}
self.create_article_with_blank_title = {
"title": "",
"description": "he wows the kids",
"body": "Fresh kid is a 5 year musician who has been on map."
}
self.create_article_with_no_title = {
"description": "he wows the kids",
"body": "Fresh kid is a 5 year musician who has been on map."
}
self.create_article_with_blank_description = {
"title": "fresh kid is a wiz",
"description": "",
"body": "Fresh kid is a 5 year musician who has been on map."
}
self.create_article_with_no_description = {
"title": "he wows the kids",
"body": "Fresh kid is a 5 year musician who has been on map."
}
self.create_article_with_blank_body = {
"title": "fresh kid is a wiz",
"description": "Fresh kid is a 5 year musician who has been on map",
"body": ""
}
self.create_article_with_no_body = {
"title": "he wows the kids",
"bdescription": "Fresh kid is a 5 year musician who has been on map."
}

self.update_article = {
"title": "Fresh kid concert was great",
"description": "he wowed the adults too",
"body": "Fresh kid is on the map."
}
self.short_title_article = {
"title": "F",
"description": "he wowed the adults too",
"body": "Fresh kid is on the map."
}

self.create_article_user = {
"title": "Town hall at Andela",
"description": "There was lots of fun",
"body": "There was no TIA chant."
}
Loading

0 comments on commit 1f65633

Please sign in to comment.