-
Notifications
You must be signed in to change notification settings - Fork 6
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(articles): user should access article crud
- add create article feature - add retrieve one or all their articles feature - add update article feature - add delete article feature - add unique article slugs automatically added to articles if they don't exist - add permissions so that user updates and deletes only articles they created - add jwt authentication to cover article routes for crud operations [Starts #165273477]
- Loading branch information
JEAN MARCUS
authored and
JEAN MARCUS
committed
Apr 30, 2019
1 parent
ec92350
commit 1717de8
Showing
21 changed files
with
582 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -95,3 +95,8 @@ ENV/ | |
|
||
# SQLite3 | ||
db.sqlite3 | ||
|
||
#the coverage file | ||
.coveragerc | ||
|
||
htmlcov/* |
Empty file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
from django.contrib import admin | ||
|
||
# Register your models here. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
from django.apps import AppConfig | ||
|
||
|
||
class ArticlesConfig(AppConfig): | ||
name = 'articles' |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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" | ||
} | ||
} | ||
] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
[ | ||
{ | ||
"model": "authentication.user", | ||
"pk": 1, | ||
"fields": { | ||
"username": "jeanmarcus", | ||
"email": "jean@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" | ||
} | ||
} | ||
] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,44 @@ | ||
import json | ||
|
||
# from rest_framework.renderers import JSONRenderer | ||
|
||
|
||
# class ArticleJSONRenderer(JSONRenderer): | ||
# charset = 'utf-8' | ||
|
||
# def render(self, data, media_type=None, renderer_context=None): | ||
# """ Method to render response with article key and list of dictionaries as value """ | ||
# return json.dumps({ | ||
# 'article': data | ||
# }) | ||
|
||
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 | ||
}) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,67 @@ | ||
from rest_framework.test import APIClient, APITestCase | ||
|
||
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() | ||
user = User.objects.get(username='jeanmarcus') | ||
self.client.force_authenticate(user=user) | ||
|
||
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." | ||
} | ||
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.login_data = { | ||
"user": { | ||
"email": "jean@gmail.com", | ||
"password": "kensanya1234" | ||
} | ||
} | ||
|
Oops, something went wrong.