CSSとかのコツ
CSSって、ちゃんと勉強してないので、いまいち理解できてない。他の人が作ったhtmlファイルとか、cssファイルは読解が難しい気がする。
- CSSの場合、classが重要なので、classをまず確認する。
- そのclassが、どういう仕様になっているのか、コードの記載があるcssファイル(main.css)とかで確認する。
- djangoに限って言えば、以下のようにstyleで囲んで新しいcss classを作成する。そのclassを追記すれば無理やり上書きできる。
<style> {% block style %} .btn-large { background-color: #00aeda; } .btn-blue { color: white; background-color:#00aeda; } .appIconBig{ display: block; margin-left: auto; margin-right: auto; height: 75px; width: 75px; } .appIconS{ padding: 0px; margin: 0px; height: 30px; width: 30px; vertical-align: middle; } .navbar-brand-small{ padding:0px; margin:0px; vertical-align: middle; } @import 'https://fonts.googleapis.com/css?family=Dosis|PT+Sans'; .backimage-text{ font-family:'PT Sans','Dosis', Avenir, "Open Sans", "游ゴシック", "Yu Gothic", Helvetica, sans-serif; /*font-weight: bolder;*/ } {% endblock %} </style>
cssを上書きするときに、ハイフォンでつないで、別のclassを追加するのはわかりやすいかもしれない。navbar-brandの場合は、navbar-brand-small とかいうふうに。
padding とmarginは大事。
padding 5 5 とかで、縦、横の設定が一気にできるので、楽。
padding-top, padding-bottom, padding-left, padding-right とかでも設定できる。
AngularJS1 + Django REST Frameworkに再挑戦 その12 Comment Reply Directive
概要
AngularJS1 + Django REST Frameworkに再挑戦 その12 Comment Reply Directive
参考動画
Django + AngularJS | Coding For Entrepreneurs
Reply to Comments part1
- js/app/comment/comment.directive.js を作成する。
'use strict'; angular.module('core.comment'). // Commentも読み込む directive('commentThread', function(Comment){ return { restrict: "E", scope: { }, template: "<ul><li>Reply</li></ul>", link: function(scope, element, attr){ } } })
home.htmlに、
<script src='{% static "js/app/core/comment/comment.directive.js" %}' ></script>
に読み込ませる。ここで1点ポイントは、
comment.directive.js
の後に読み込ませる。なぜなら、comment.directive.js
内の、Commentモジュールを継承したいため。blog-detail.htmlに、
<comment-thread></commentThread>
を追記することで、commentThreadを利用できる。最終的に以下のようにcomment.directive.jsを修正する。
angular.module('core.comment'). directive('commentReplyThread', function(Comment){ return { restrict: "E", scope: { comment: '=comment', }, template: "<ul ng-show='replies'><li ng-repeat='reply in replies'>{{ reply.content }}</li></ul>" + "<div class='text-center' ng-show='!replies'><img style='margin: 0 auto;' ng-src='/static/img/ring.gif' class='img-responsive'/><div>", // ng-src='/static/img/ring.gif'ここで、アニメーションがちょっと使える! // loading.ioという所のgifアニメを使うといいぽいな。 link: function(scope, element, attr){ if (scope.comment){ var commentId = scope.comment.id if (commentId){ Comment.get({id: commentId}, function(data){ scope.replies = data.replies }) } } } } })
Reply to Comments part2
- blog-detailの一部を、comment.directive.jsのtemplateに埋め込む
'use strict'; angular.module('core.comment'). directive('commentReplyThread', function(Comment){ return { restrict: "E", scope: { comment: '=comment', }, template: "<ul ng-show='replies'><li ng-repeat='reply in replies'>{{ reply.content }}</li></ul>" + "<div class='text-center' ng-show='!replies'><img style='margin: 0 auto;' ng-src='/static/img/ring.gif' class='img-responsive'/>" + "</div>" + "<p style='color:red;' ng-if='reply.content'>Preview: {{ reply.content }}</p>" + "<form ng-submit='addCommentReply(reply, comment)'>" + "<textarea class='form-control' ng-model='reply.content'></textarea>" + "<input class='btn btn-default btn-sm' type='submit' value='Reply'/>" + "</form>", // ng-src='/static/img/ring.gif'ここで、アニメーションがちょっと使える! // loading.ioという所のgifアニメを使うといいぽいな。 link: function(scope, element, attr){ if (scope.comment){ var commentId = scope.comment.id if (commentId){ Comment.get({id: commentId}, function(data){ scope.replies = data.replies }) } } } } })
- 一旦indexpageなどに直接コードをかく->angularのtemplateに一部埋め込む->angularのtemplateのhtmlを作成する という流れが一番わかりやすくてミスが少ないかもしれない。
AngularJS1 + Django REST Frameworkに再挑戦 その11 Reply to Comments
概要
AngularJS1 + Django REST Frameworkに再挑戦 その11 Reply to Comments
参考動画
Django + AngularJS | Coding For Entrepreneurs
Reply to Comments
- コメントのreplyとかを表示したい。まずはcommentのcontextの内容を確認する。
<ul ng-show='comments.length > 0'> <li ng-repeat='comment in comments | filter: query'> {{ comment.content }} | Replies: {{ comment.reply_count }} | <a href='#' class='btn btn-sm btn-default' confirm-click='Do you want to delete this?' confirmed-click='deleteComment(comment)'>X</a> </li> </ul>
- さらにcommentの送信ボックスなどを作る。
<ul ng-show='comments.length > 0'> <li ng-repeat='comment in comments | filter: query'> {{ comment.content }}<br/>| <small>Replies: {{ comment.reply_count }} |<a href='#' class='' confirm-click='Do you want to delete this?' confirmed-click='deleteComment(comment)'>Remove</a></small> <br/> <div class='row'> <div class='col-sm-6'> <p style='color:red;' ng-if='reply.content'>Preview: {{ reply.content }}</p> <form ng-submit='addReply()'> <textarea class='form-control' ng-model='reply.content'></textarea> <input class='btn btn-default btn-sm' type='submit' value='Reply'/> </form> </div> </div> <div class='clearfix'></div> <br/> <br/> </li> </ul>
- commentのreplyのformに、parent_idが自動で入力されるようにする。
<form ng-submit='addReply()'> <input type='hidden' ng-model='reply.parent_id' value='{{ comment.id }}'> <textarea class='form-control' ng-model='reply.content'></textarea> <input class='btn btn-default btn-sm' type='submit' value='Reply'/> </form>
- blog-detail.component.jsのaddReplyに追記してみる。
$scope.addReply = function() { console.log($scope.reply)
- このままではうまく行かないので、addCommentReplyというfunctionをblog-detail.component.jsに追記する。
$scope.addCommentReply = function(reply, parentComment){ console.log(reply) console.log(parentComment.id) } //これはとりあえず、replyの内容と、parentComment.idの内容をconsolelogに表示するだけ。
- blog-detail.htmlに追記する。addCom
<form ng-submit='addCommentReply(reply, comment)'> <textarea class='form-control' ng-model='reply.content'></textarea> <input class='btn btn-default btn-sm' type='submit' value='Reply'/> </form>
- blog-detail.htmlに追記する。addNewCommentというfunctionに変更する。
<div class='row'> <div class='col-sm-6'> <p style='color:red;' ng-if='newComment.content'>Preview: {{ newComment.content }}</p> <form ng-submit='addNewComment()'> <textarea class='form-control' ng-model='newComment.content'></textarea> <input class='btn btn-default' type='submit'/> </form> </div> </div>
- blog-detail.component.jsにaddNewCommentを作成する。
$scope.addNewComment = function() { // console.log($scope.reply) Comment.create({ content: $scope.newComment.content, slug: slug, type: "post" }, function(data){ //console.log(data) $scope.comments.push(data) resetReply() }, function(e_data){ // error console.log(e_data) }) }
AngularJS1 + Django REST Frameworkに再挑戦 その10 ngResource Create & Delete
概要
AngularJS1 + Django REST Frameworkに再挑戦 その10 ngResource Create & Delete
参考動画
Django + AngularJS | Coding For Entrepreneurs
ngResource Create & Delete
ngResource を使って、commentをsave, deleteする機能を実装する。
blog-detail.component.jsを編集する。Commentのsave機能をつける。
... Comment.save({ //saveに必要な項目を代入する。 content: $scope.reply.content, slug: slug, type: "post" }, //ここからは、dataと、e_dataがある場合は、error_dataを表示するようなfunctionをセットしてある。 function(data){ // success console.log(data) }, function(e_data){ // error console.log(e_data) })
- これで、commentをsaveする機能がついた。あとは、今までのcomment createのコードをコメントアウトする。
... $scope.addReply = function() { console.log($scope.reply) var token = $cookies.get("token") if (token){ Comment.save({ content: $scope.reply.content, slug: slug, type: "post" }, function(data){ // success console.log(data) }, function(e_data){ // error console.log(e_data) }) // var req = { // method: "POST", // url: 'http://127.0.0.1:8000/api/comments/create/', // data:{ // content: $scope.reply.content, // slug: slug, // type: "post", // }, // headers:{ // authorization: "JWT " + token // } // } // // var requestAction = $http(req) // // requestAction.success(function(r_data, r_status, r_headers, r_config){ // $scope.comments.push($scope.reply) // resetReply() // }) // requestAction.error(function(e_data,e_status, e_headers, e_config){ // console.log(e_data) // }) // // $scope.comments.push($scope.reply) // // $scope.post.comments.push("abc") } else { ...
Failed to load resource: the server responded with a status of 405 (Method Not Allowed)
このままだとこのerrorが出てしまうので、ちょっと修正が必要。GETではなく、POSTでデータを送付する必要あり。全体を修正する。comment.service.jsに、commentCreateメソッドを追加する。
'use strict'; angular. module('core.comment'). factory('Comment', function($resource){ // こちらのurlは、commentのurlにセットする。 var url = '/api/comments/:id/' var commentQuery = { url: url, method: "GET", params: {}, isArray: true, cache: false, transformResponse: function(data, headerGetter, status){ // console.log(data) var finalData = angular.fromJson(data) return finalData.results } } var commentGet = { method: "GET", params: {"id": "@id"}, isArray: false, cache: false, } var commentCreate = { //こちらのurlは、djangoで設定したcommentsをcreateするurlを指定する。 url: '/api/comments/create/', method: "POST", // params: {"id": "@id"}, // isArray: false, // cache: false, } return $resource(url, {}, { query: commentQuery, get: commentGet, create: commentCreate, }) });
- headerの
statusText:"Unauthorized"というerrorが出て保存ができない!
comment.service.js`で、コメントアウトしたところに、headerにauthorizationを入れるコードがあったと思うので、それを利用して、認証をする。
... factory('Comment', function($cookies, $resource){ ... // cookiesからtokenをgetする。 var token = $cookies.get("token") if(token){ // headersにJWT tokenを代入する。 commentCreate["headers"] = {"Authorization": "JWT " + token} }
- これで一応は、登録ができるようになったけど、一度更新しないと、comment一覧に反映されないのがいまいち!なので、blog-detailページの処理を担当している、
blog-detail.compose.js
を修正する。
if (token){ Comment.create({ content: $scope.reply.content, slug: slug, type: "post" }, function(data){ // success //console.log(data) //ここを追加する $scope.comments.push(data) }, function(e_data){ // error console.log(e_data) })
これでリアルタイムに反映されるようになった!
次はdelete機能をつけていく。試しに
blog-detail.component.js
に、シンプルなdelete機能を追記してみる。
$scope.deleteComment = function(comment) { comment.$delete() // $scope.$apply( // $scope.comments.splice(comment, 1) // ) // someResource.$delete() }
- これで、deleteを試すと、
405 (Method Not Allowed)
が表示されてしまう。なので、createの時と同じように、comment.compose.js
に、commentDeleteメソッドを作成する。
... var commentDelete = { // urlは、commentsのidを選択する。 url: '/api/comments/:id/', // methodにDELETEを指定する。 method: "DELETE", // params: {"id": "@id"}, // isArray: false, // cache: false, } ... var token = $cookies.get("token") if(token){ commentCreate["headers"] = {"Authorization": "JWT " + token} //ここを追記する。 commentDelete["headers"] = {"Authorization": "JWT " + token} } return $resource(url, {}, { query: commentQuery, get: commentGet, create: commentCreate, delete: commentDelete })
- これで試してみると、
error
が表示されてしまう。どうも、comment_idが不明らしいということなので、deleteの際に、id
を追記する。blog-detail.component.jsを修正する。
... $scope.deleteComment = function(comment) { comment.$delete({"id": comment.id}) // $scope.$apply( // $scope.comments.splice(comment, 1) // ) // someResource.$delete() } ...
- これで一旦削除はできるようになった!次は、リアルタイムで画面から削除するコードを追記する。javascriptの、spliceメソッドを使う。多分これは、リストから指定した数の要素を削除するとかそういう意味だと思う。
$scope.deleteComment = function(comment) { comment.$delete({"id": comment.id}, function(data){ // success $scope.comments.splice(comment,1) }, function(e_data){ // error console.log(e_data) })
- 同じようにupdateReplyもaddReplyと似たような形で、実装できる。まずは、blog-detail.component.jsを修正する。
$scope.updateReply = function(comment) { Comment.update({ "id": comment.id, content: $scope.reply.content, slug: slug, type: "post" }, function(data){ //console.log(data) //$scope.comments.push(data) // resetReply() }, function(e_data){ // error console.log(e_data) }) }
- 次に、comment.service.jsを修正する。
... // まず、commentUpdateを作成する。 var commentUpdate = { url: '/api/comments/:id/', method: "PUT", // params: {"id": "@id"}, // isArray: false, // cache: false, } ... // 次に認証を作成する。 var token = $cookies.get("token") if(token){ commentCreate["headers"] = {"Authorization": "JWT " + token} commentDelete["headers"] = {"Authorization": "JWT " + token} commentUpdate["headers"] = {"Authorization": "JWT " + token} } ... // 最後にプロパティとして、登録する。 return $resource(url, {}, { query: commentQuery, get: commentGet, create: commentCreate, delete: commentDelete, update: commentUpdate, }) ...
- 結構ややこしいな。重要なのは、viewを修正することと、モデル(データ)を修正することは別々の機能として考えた方がわかりやすいということだね。
AngularJS1 + Django REST Frameworkに再挑戦 その9 Comment ngResource
概要
AngularJS1 + Django REST Frameworkに再挑戦 その9 Comment ngResource
参考動画
Django + AngularJS | Coding For Entrepreneurs
Comment ngResource
ngResource は、$httpを含んでおり、サーバとHTTP通信でデータのやりとりをするものなのかな。
core/post/post.module.jsのモジュール名を、post->core.post に名前を変更する(よりロバストにするため。)
core/comment フォルダを作成。
core/comment/comment.module.jsと、comment.service.jsを作成する。
comment.module.jsにコードを追記する。
'use strict'; angular.module('core.comment', []);
- comment.service.jsを作成する。
'use strict'; angular. module('core.comment'). factory('Comment', function($resource){ var url = '/api/comments/' return $resource(url, {}, { query: { method: "GET", params: {}, isArray: true, cache: false, transformResponse: function(data, headerGetter, status){ // console.log(data) var finalData = angular.fromJson(data) return finalData.results } // interceptor }, get: { method: "GET", params: {"slug": "@slug"}, isArray: false, cache: false, } }) });
まず、
var url = '/api/comments/'
に変更する。commentのjsonデータをゲットするために、
var commentQuery
をセットする。
var commentQuery = { url: url, method: "GET", params: {}, isArray: true, cache: false, transformResponse: function(data, headerGetter, status){ // console.log(data) var finalData = angular.fromJson(data) return finalData.results }
- 同様に
var commentGet
をセットする。ここで注意すべきなのは、url + ":id/"
としているところ。
var commentGet = { url: url + ":id/" method: "GET", params: {"id": "@id"}, isArray: false, cache: false, }
全体のデータのゲットは、commentQueryで、個別コメントのゲットは、commentGetでJSONデータを取得するのか。ということはcommentGetの方でもtransformResponseが必要になりそうだけど、どうなんだろうな。
現在はcomment.service.jsは、以下のようなコードになっている。
'use strict'; angular. module('core.comment'). factory('Comment', function($resource){ var url = '/api/comments/' var commentQuery = { url: url, method: "GET", params: {}, isArray: true, cache: false, transformResponse: function(data, headerGetter, status){ // console.log(data) var finalData = angular.fromJson(data) return finalData.results } } var commentGet = { url: url + ":id/" method: "GET", params: {"id": "@id"}, isArray: false, cache: false, } return $resource(url, {}, { query: commentQuery, get: commentGet, }) });
- core.commentをblog-detail.component.jsに組み込んでみる。
... angular.module('blogDetail'). component('blogDetail', { templateUrl: '/api/templates/blog-detail.html', controller: function(Comment, Post, $cookies, $http, $location, $routeParams, $scope){ var slug = $routeParams.slug Post.get({"slug": slug}, function(data){ $scope.post = data // $scope.comments = data.comments // Commentをslugと、type(contenttype)で絞り込んでおく。 Comment.query({"slug": slug, "type":"post"}, function(data){ $scope.comments = data }) }) ...
AngularJS1 + Django REST Frameworkに再挑戦 その8 Comment ListAPI View
概要
AngularJS1 + Django REST Frameworkに再挑戦 その8 Comment ListAPI View
参考動画
Django + AngularJS | Coding For Entrepreneurs
Comment ListAPI View
- blog-detai.component.jsのaddReplyを少し修正する。
$scope.addReply = function() { console.log($scope.reply) var token = $cookies.get("token") if (token){ var req = { method: "POST", url: 'http://127.0.0.1:8000/api/comments/create/', data:{ content: $scope.reply.content, slug: slug, type: "post", }, headers:{ authorization: "JWT " + token } }
- ただし、settings.pyの
rest_framework.authentication.SessionAuthentication
をコメントアウトしておかないと、なぜかcommentができない。
"DEFAULT_AUTHENTICATION_CLASSES": ( # 'rest_framework.authentication.SessionAuthentication', 'rest_framework_jwt.authentication.JSONWebTokenAuthentication', #'rest_framework.authentication.BasicAuthentication' ),
- CommentListAPIを修正する。
from django.contrib.contenttypes.models import ContentType ... class CommentListAPIView(ListAPIView): serializer_class = CommentListSerializer permission_classes = [AllowAny] filter_backends= [SearchFilter, OrderingFilter] search_fields = ['content', 'user__first_name'] pagination_class = PostPageNumberPagination #PageNumberPagination def get_queryset(self, *args, **kwargs): #queryset_list = super(PostListAPIView, self).get_queryset(*args, **kwargs) queryset_list = Comment.objects.filter(id__gte=0) #filter(user=self.request.user) query = self.request.GET.get("q") slug = self.request.GET.get("slug") # content we commented on type = self.request.GET.get("type", "post") # content type if slug: model_type = type model_qs = ContentType.objects.filter(model=model_type) if model_qs.exists(): SomeModel = model_qs.first().model_class() obj_qs = SomeModel.objects.filter(slug=slug) if obj_qs.exists(): content_obj = obj_qs.first() queryset_list = Comment.objects.filter_by_instance(content_obj) if query: queryset_list = queryset_list.filter( Q(content__icontains=query)| Q(user__first_name__icontains=query) | Q(user__last_name__icontains=query) ).distinct() return queryset_list ...
何がしたいかというと、
- postのdetailpageに表示されるslugを取得->postのinstanceを絞り込む->そのinstanceで、commentを絞り込む
- content-typeが、postのqueryset(要は、slugでpostを絞り込む)
- さらにその中から、postのinstanceで、コメントを絞り込む。このinstanceで絞り込むところが多分ポイント。
- slugでそのまま絞り込んでも良さそうなきがするけど、ダメなのかな。
さらに修正する。
queryset_list = []
をセットすることと、slugがないときにelseで、空のリストを返すように設定してある。
class CommentListAPIView(ListAPIView): ... def get_queryset(self, *args, **kwargs): #queryset_list = super(PostListAPIView, self).get_queryset(*args, **kwargs) queryset_list = [] query = self.request.GET.get("q") slug = self.request.GET.get("slug") # content we commented on type = self.request.GET.get("type", "post") # content type if slug: model_type = type model_qs = ContentType.objects.filter(model=model_type) if model_qs.exists(): SomeModel = model_qs.first().model_class() obj_qs = SomeModel.objects.filter(slug=slug) if obj_qs.exists(): content_obj = obj_qs.first() queryset_list = Comment.objects.filter_by_instance(content_obj) else: queryset_list = Comment.objects.filter(id__gte=0) #filter(user=self.request.user) if query: queryset_list = queryset_list.filter( Q(content__icontains=query)| Q(user__first_name__icontains=query) | Q(user__last_name__icontains=query) ).distinct() return queryset_list
AngularJS1 + Django REST Frameworkに再挑戦 その7 Comment Create API Endpoint & Serializer Context
概要
AngularJS1 + Django REST Frameworkに再挑戦 その7 Comment Create API Endpoint & Serializer Context
参考動画
Django + AngularJS | Coding For Entrepreneurs
Comment Create API Endpoint & Serializer Context
http://127.0.0.1:8000/api/comments/create/
にアクセスして、apiを作成しようとしても、valid errorが表示されて、作成できない。なので、serializerを変更する。comments/api/serializers.pyの、
class CommentCreateSerializer(ModelSerializer):
をコピペして独立させる。以下のように追記する。
from rest_framework import serializers ... class CommentCreateSerializer(ModelSerializer): type = serializers.CharField(write_only=True) #Post slug = serializers.SlugField(write_only=True) #new-title parent_id = serializers.IntergerField(required=False) # comment自体が親になる可能性もあるため ...
def __init__
の初期化メソッドの箇所は丸々削除する。class Meta
のfieldsを追記する。後、validate以下は一旦コメントアウトする。
fields = [ 'id', 'content', 'type', 'slug', 'parent_id', 'timestamp', ]
まあ普通のserializerに戻したという感じで、ここから新たにserializerの機能を追加していく。
def validate
を以下のように修正する。
def validate(self, data): model_type = data.get("type", "post") model_qs = ContentType.objects.filter(model=model_type) if not model_qs.exists() or model_qs.count() != 1: raise ValidationError("This is not a valid content type") SomeModel = model_qs.first().model_class() slug = data.get("slug") obj_qs = SomeModel.objects.filter(slug=slug) if not obj_qs.exists() or obj_qs.count() != 1: raise ValidationError("This is not a slug for this content type") parent_id = data.get("parent_id") if parent_id: parent_qs = Comment.objects.filter(id=parent_id) if not parent_qs.exists() or parent_qs.count() != 1: raise ValidationError("This is not a valid parent for this content") return data
- def createも同じように、validated_dataを利用して、書き換える。
def create(self, validated_data): content = validated_data.get("content") if user: main_user = user else: main_user = User.objects.all().first() model_type = validated_data.get("type", "post") slug = validated_data.get("slug") parent_id = validated_data.get("parent_id") parent_obj = None if parent_id: parent_obj = Comment.objects.filter(id=parent_id).first() comment = Comment.objects.create_by_model_type( model_type, slug, content, main_user, parent_obj=parent_obj, ) return comment
- comments/api/views.pyのCommentCreateAPIViewを修正する。一旦シンプルな形に戻す。
class CommentCreateAPIView(CreateAPIView): queryset = Comment.objects.all() serializer_class = CommentCreateSerializer
- CommentCreateAPIViewに、contextを追記する。(get_serializer_contextを上書きする)
class CommentCreateAPIView(CreateAPIView): queryset = Comment.objects.all() serializer_class = CommentCreateSerializer def get_serializer_context(self): context = super().get_serializer_context() context['user'] = self.request.user return context
- なぜ、userをわざわざcontextで渡すかというと、CommentCreateSerializerの方で、使いたいらしい。
def create(self, validated_data): content = validated_data.get("content") model_type = validated_data.get("type", "post") slug = validated_data.get("slug") parent_id = validated_data.get("parent_id") parent_obj = None if parent_id: parent_obj = Comment.objects.filter(id=parent_id).first() user = self.context['user'] comment = Comment.objects.create_by_model_type( model_type, slug, content, user, parent_obj=parent_obj, ) return comment
- ここまでセットしたら、
http://127.0.0.1:8000/api/comments/create/
でコメントを作成してみる。。。。無事表示された〜!