From b6891f1321ec758c1b45b8ac9a9d8fe7b5dfb459 Mon Sep 17 00:00:00 2001 From: Rushil Umaretiya <rushilwiz@gmail.com> Date: Sat, 15 Aug 2020 05:39:48 -0400 Subject: [PATCH] created class based views --- news/admin.py | 4 +- news/migrations/0001_initial.py | 27 ++++++++ news/models.py | 16 +++++ news/templates/news/about.html | 2 +- .../news/article_confirm_delete.html | 27 ++++++++ news/templates/news/article_detail.html | 19 ++++++ news/templates/news/article_form.html | 16 +++++ news/templates/news/base.html | 20 +----- news/templates/news/home.html | 36 +++++++++- news/templates/news/user_article.html | 37 ++++++++++ news/urls.py | 18 ++++- news/views.py | 68 ++++++++++++++++++- 12 files changed, 264 insertions(+), 26 deletions(-) create mode 100644 news/migrations/0001_initial.py create mode 100644 news/templates/news/article_confirm_delete.html create mode 100644 news/templates/news/article_detail.html create mode 100644 news/templates/news/article_form.html create mode 100644 news/templates/news/user_article.html diff --git a/news/admin.py b/news/admin.py index 8c38f3f..579e9db 100644 --- a/news/admin.py +++ b/news/admin.py @@ -1,3 +1,5 @@ from django.contrib import admin - +from .models import Article # Register your models here. + +admin.site.register(Article) diff --git a/news/migrations/0001_initial.py b/news/migrations/0001_initial.py new file mode 100644 index 0000000..8d2a6fa --- /dev/null +++ b/news/migrations/0001_initial.py @@ -0,0 +1,27 @@ +# Generated by Django 3.1 on 2020-08-15 09:24 + +from django.conf import settings +from django.db import migrations, models +import django.db.models.deletion + + +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')), + ('content', models.TextField()), + ('date_published', models.DateTimeField(auto_now_add=True)), + ('likes', models.IntegerField(default=0)), + ('author', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)), + ], + ), + ] diff --git a/news/models.py b/news/models.py index 71a8362..c3da620 100644 --- a/news/models.py +++ b/news/models.py @@ -1,3 +1,19 @@ +# Create your models here. from django.db import models +from django.utils import timezone +from django.contrib.auth.models import User +from django.urls import reverse # Create your models here. + +class Article(models.Model): + content = models.TextField() + date_published = models.DateTimeField(auto_now_add=True) + author = models.ForeignKey(User, on_delete=models.CASCADE) + likes = models.IntegerField(default=0) + + def __str__(self): + return f"{self.author}'s article on {self.date_published}" + + def get_absolute_url(self): + return reverse('article-detail', kwargs={'pk': self.pk}) diff --git a/news/templates/news/about.html b/news/templates/news/about.html index b2c6d79..8dabb47 100644 --- a/news/templates/news/about.html +++ b/news/templates/news/about.html @@ -1,4 +1,4 @@ {% extends 'news/base.html' %} {% block content %} -<h1>about</h1> +<h1>home</h1> {% endblock content %} diff --git a/news/templates/news/article_confirm_delete.html b/news/templates/news/article_confirm_delete.html new file mode 100644 index 0000000..2ba177b --- /dev/null +++ b/news/templates/news/article_confirm_delete.html @@ -0,0 +1,27 @@ +{% extends "news/base.html" %} +{% block content %} +<div class="content-section"> + <form method="POST"> + {% csrf_token %} + <fieldset class="form-group"> + <legend class="border-bottom mb-4"> Delete article </legend> + <h2>Are you sure you want to delete this article?</h2> + <article class="media content-section"> + <img class="rounded-circle article-img" src="{{ object.author.profile.profile_pic.url}}" alt="Profile Picture"> + <div class="media-body"> + <div class="article-metadata"> + <a class="mr-2" href="#">{{ object.author.get_full_name }}</a> + <small class="text-muted">@{{ object.author }} · {{ object.date_posted }}</small> + </div> + <p class="article-content font-weight-light">{{ article.content }}</p> + </div> + </article> + </fieldset> + <div class="form-group"> + <button type="submit" class="btn btn-outline-danger">Yes, Delete</button> + <a type="submit" class="btn btn-outline-secondary" href="{% url 'article-detail' object.id %}">No, take me back</a> + </div> + </form> +</div> +</div> +{% endblock content %} diff --git a/news/templates/news/article_detail.html b/news/templates/news/article_detail.html new file mode 100644 index 0000000..0ee9e1b --- /dev/null +++ b/news/templates/news/article_detail.html @@ -0,0 +1,19 @@ +{% extends "news/base.html" %} +{% block content %} + <article class="media content-section"> + <img class="rounded-circle article-img" src="{{ object.author.profile.profile_pic.url}}" alt="Profile Picture"> + <div class="media-body"> + <div class="article-metadata"> + <a class="mr-2" href="{% url 'user-articles' object.author.username %}">{{ object.author.get_full_name }}</a> + <small class="text-muted">@{{ object.author }} · {{ object.date_published }}</small> + {% if object.author == user %} + <div> + <a class="btn btn-secondary btn-sm mt-1 mb-1" href="{% url 'article-update' object.id %}">Edit article</a> + <a class="btn btn-danger btn-sm mt-1 mb-1" href="{% url 'article-delete' object.id %}">Delete article</a> + </div> + {% endif %} + </div> + <p class="article-content font-weight-light">{{ article.content }}</p> + </div> + </article> +{% endblock content %} diff --git a/news/templates/news/article_form.html b/news/templates/news/article_form.html new file mode 100644 index 0000000..41f5398 --- /dev/null +++ b/news/templates/news/article_form.html @@ -0,0 +1,16 @@ +{% extends "news/base.html" %} +{% load crispy_forms_tags %} +{% block content %} +<div class="content-section"> + <form method="post"> + {% csrf_token %} + <fieldset class="form-group"> + {{ form | crispy}} + </fieldset> + <div class="form-group"> + <button type="submit" class="btn btn-outline-info">Submit article</button> + </div> + </form> +</div> +</div> +{% endblock content %} diff --git a/news/templates/news/base.html b/news/templates/news/base.html index 5e9dfd5..fc2c289 100644 --- a/news/templates/news/base.html +++ b/news/templates/news/base.html @@ -15,7 +15,7 @@ <!-- Favicon --> <link rel="shortcut icon" type="image/png" href="{% static 'favicon.ico' %}"/> - <title>Chirper</title> + <title>NewViewsNews</title> </head> <body> @@ -35,7 +35,7 @@ <!-- Navbar Right Side --> <div class="navbar-nav"> {% if user.is_authenticated %} - <a class="nav-item nav-link" href="">New Chirp</a> + <a class="nav-item nav-link" href="{% url 'article-create' %}">Write</a> <a class="nav-item nav-link" href="{% url 'profile' %}">{{ user.username }}</a> <a class="nav-item nav-link" href="{% url 'logout' %}">Logout</a> {% else %} @@ -47,11 +47,9 @@ </nav> </header> - <img src="{% static 'news/css/res/image.png' %}" alt=""> - <main role="main" class="container"> <div class="row"> - <div class="col-md-8"> + <div class="col-xl"> {% if messages %} {% for message in messages %} <div class="alert alert-{{ message.tags }} alert-dismissible fade show"> @@ -64,18 +62,6 @@ {% endif %} {% block content %}{% endblock %} </div> - <div class="col-md-4"> - <div class="content-section"> - <h3>Trending</h3> - <ul class="list-group"> - <li class="list-group-item"><a href="https://www.djangoproject.com/">#Django</a></li> - <li class="list-group-item"><a href="https://tjctf.org">#TJCTF</a></li> - <li class="list-group-item"><a href="https://sysadmins.tjhsst.edu/understudy/">#Understudy</a></li> - <li class="list-group-item"><a href="https://dadjokegenerator.com/">#DadJokes</a></li> - </ul> - </p> - </div> - </div> </div> </div> </main> diff --git a/news/templates/news/home.html b/news/templates/news/home.html index 8dabb47..c80f3ea 100644 --- a/news/templates/news/home.html +++ b/news/templates/news/home.html @@ -1,4 +1,36 @@ -{% extends 'news/base.html' %} +{% extends "news/base.html" %} {% block content %} -<h1>home</h1> + {% for article in articles %} + <article class="media content-section"> + <img class="rounded-circle article-img" src="{{ article.author.profile.profile_pic.url}}" alt="Profile Picture"> + <div class="media-body"> + <div class="article-metadata"> + <a class="mr-2" href="{% url 'user-articles' article.author.username %}">{{ article.author.get_full_name }}</a> + <small class="text-muted">@{{ article.author }} · {{ article.date_posted }}</small> + </div> + <p class="article-content font-weight-light"><a class="nounderline" href="{% url 'article-detail' article.id %}">{{ article.content }}</a></p> + </div> + </article> + {% endfor %} + {% if is_paginated %} + + {% if page_obj.has_previous %} + <a class="btn btn-outline-info mb-4" href="?page=1">First</a> + <a class="btn btn-outline-info mb-4" href="?page={{ page_obj.previous_page_number }}">Previous</a> + {% endif %} + + {% for num in page_obj.paginator.page_range %} + {% if page_obj.number == num %} + <a class="btn btn-info mb-4" href="?page={{ num }}">{{ num }}</a> + {% elif num > page_obj.number|add:'-3' and num < page_obj.number|add:'3' %} + <a class="btn btn-outline-info mb-4" href="?page={{ num }}">{{ num }}</a> + {% endif %} + {% endfor %} + + {% if page_obj.has_next %} + <a class="btn btn-outline-info mb-4" href="?page={{ page_obj.next_page_number }}">Next</a> + <a class="btn btn-outline-info mb-4" href="?page={{ page_obj.paginator.num_pages }}">Last</a> + {% endif %} + + {% endif %} {% endblock content %} diff --git a/news/templates/news/user_article.html b/news/templates/news/user_article.html new file mode 100644 index 0000000..a279c41 --- /dev/null +++ b/news/templates/news/user_article.html @@ -0,0 +1,37 @@ +{% extends "news/base.html" %} +{% block content %} + <h1 class="mb-3">Articles by {{ view.kwargs.username }} ({{ page_obj.paginator.count }})</h1></h1> + {% for article in articles %} + <article class="media content-section"> + <img class="rounded-circle article-img" src="{{ article.author.profile.profile_pic.url}}" alt="Profile Picture"> + <div class="media-body"> + <div class="article-metadata"> + <a class="mr-2" href="{% url 'user-articles' article.author.username %}">{{ article.author.get_full_name }}</a> + <small class="text-muted">@{{ article.author }} · {{ article.date_published }}</small> + </div> + <p class="article-content font-weight-light"><a class="nounderline" href="{% url 'article-detail' article.id %}">{{ article.content }}</a></p> + </div> + </article> + {% endfor %} + {% if is_paginated %} + + {% if page_obj.has_previous %} + <a class="btn btn-outline-info mb-4" href="?page=1">First</a> + <a class="btn btn-outline-info mb-4" href="?page={{ page_obj.previous_page_number }}">Previous</a> + {% endif %} + + {% for num in page_obj.paginator.page_range %} + {% if page_obj.number == num %} + <a class="btn btn-info mb-4" href="?page={{ num }}">{{ num }}</a> + {% elif num > page_obj.number|add:'-3' and num < page_obj.number|add:'3' %} + <a class="btn btn-outline-info mb-4" href="?page={{ num }}">{{ num }}</a> + {% endif %} + {% endfor %} + + {% if page_obj.has_next %} + <a class="btn btn-outline-info mb-4" href="?page={{ page_obj.next_page_number }}">Next</a> + <a class="btn btn-outline-info mb-4" href="?page={{ page_obj.paginator.num_pages }}">Last</a> + {% endif %} + + {% endif %} +{% endblock content %} diff --git a/news/urls.py b/news/urls.py index 7c61ae8..e45daed 100644 --- a/news/urls.py +++ b/news/urls.py @@ -1,9 +1,21 @@ -from django.contrib import admin from django.urls import path - +from .views import ( + ArticleListView, + ArticleDetailView, + ArticleCreateView, + ArticleUpdateView, + ArticleDeleteView, + UserArticleListView, +) from . import views urlpatterns = [ path('', views.home, name='home'), - path('about/', views.about, name='about') + path('about/', views.about, name='about'), + + path('user/<str:username>', UserArticleListView.as_view(), name='user-articles'), + path('article/<int:pk>/', ArticleDetailView.as_view(), name='article-detail'), + path('article/new/', ArticleCreateView.as_view(), name='article-create'), + path('article/<int:pk>/update/', ArticleUpdateView.as_view(), name='article-update'), + path('article/<int:pk>/delete/', ArticleDeleteView.as_view(), name='article-delete'), ] diff --git a/news/views.py b/news/views.py index 287642e..506201c 100644 --- a/news/views.py +++ b/news/views.py @@ -1,6 +1,18 @@ -from django.shortcuts import render +from django.shortcuts import render, get_object_or_404 +from django.contrib.auth.models import User +from django.contrib.auth.mixins import LoginRequiredMixin, UserPassesTestMixin +from django.views.generic import ( + ListView, + DetailView, + CreateView, + UpdateView, + DeleteView, +) + +from .models import Article from users.models import Profile + # Create your views here. def about (request): @@ -10,4 +22,56 @@ def home (request): if request.user.is_authenticated and Profile.objects.filter(user=request.user).count() < 1: Profile.objects.create(user=request.user).save() - return render (request, 'news/home.html') + context = { + 'articles': Article.objects.all() + } + + return render (request, 'news/home.html', context) + +class ArticleListView(ListView): + model = Article + template_name = "news/home.html" + context_object_name='articles' + ordering = ['-date_posted'] + paginate_by = 8 + +class UserArticleListView(ListView): + model = Article + template_name = "news/user_articles.html" + context_object_name='articles' + paginate_by = 8 + + def get_queryset(self): + user = get_object_or_404(User, username=self.kwargs.get('username')) + return Article.objects.filter(author=user).order_by('-date_posted') + +class ArticleDetailView(DetailView): + model = Article + +class ArticleCreateView(LoginRequiredMixin, CreateView): + model = Article + fields=['content'] + + def form_valid(self, form): + form.instance.author = self.request.user + return super().form_valid(form) + +class ArticleUpdateView(LoginRequiredMixin, UserPassesTestMixin, UpdateView): + model = Article + fields=['content'] + + def form_valid(self, form): + form.instance.author = self.request.user + return super().form_valid(form) + + def test_func(self): + article = self.get_object() + return self.request.user == article.author + +class ArticleDeleteView(LoginRequiredMixin, UserPassesTestMixin, DeleteView): + model = Article + success_url='/' + + def test_func(self): + article = self.get_object() + return self.request.user == article.author