I want to add the functionality of adding comments in the same html page with post details starting from this class based view.
I also have left below the model and the html template that I want to use.
Please help me
The class based view that I want to use as a starting point :
class PostDetailView(DetailView):
model = Post
The model:
class Comments(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE)
post = models.ForeignKey(Post, on_delete=models.CASCADE)
content = models.TextField()
date_added = models.DateTimeField(default=timezone.now)
def __str__(self):
return self.content
The html template:
<div id="comments">
<h3>{{ comments.total }}</h3>
<ol class="commentlist">
{% for comment in comments %}
<li class="depth-1">
<div class="avatar">
<img width="50" height="50" alt=""
src="{{ comment.user.profile.image.url }}"
class="avatar">
</div>
<div class="comment-content">
<div class="comment-info">
<cite>{{ comment.user }}</cite>
<div class="comment-meta">
<time datetime="2014-07-12T23:05"
class="comment-time">{{ comment.date_added|date:"F d, Y" }}</time>
</div>
</div>
<div class="comment-text">
<p>{{ comment.content }}</p>
</div>
</div>
</li>
{% endfor %}
</ol> <!-- /commentlist -->
<!-- respond -->
<div class="respond">
<h3>Leave a Comment</h3>
<!-- form -->
<form action="" method="POST">
{% csrf_token %}
<fieldset>
<div class="group">
{{ form|crispy }}
<button class="submit full-width" type="submit">Add Comment</button>
</div>
</fieldset>
</form> <!-- /contactForm -->
</div> <!-- /respond -->
</div> <!-- /comments -->
Override DetailView's get_context_data method to transfer comments through context. add this method in your class PostDetailView.
def get_context_data(self, **kwargs):
context = {}
if self.object:
context['object'] = self.object
context['comments']=Comments.objects.all() #modify this queryset according to how you want to display comments
context.update(kwargs)
return super().get_context_data(**context)
than in template you can include this piece of code
{% for comment in comments%}
<div>
{{comment}}
</div>
{% endfor %}
and to get comments, In forms.py
from django import forms
from .models import Comments
class CommentForm(forms.Form):
class Meta:
model = Comments
fields = [] #mention fields of comments u want to render in form
you have to extend FormView along with DetailView as DetailView dosent have post method to override.
So in views.py:
from .forms imoprt CommentForm
from django.views.generic.edit import FormView
class PostDetailView(DetailView, FormView):
model = Post
form_class = CommentForm
def post(self, request, *args, **kwargs):
form = self.get_form()
if form.is_valid():
#do what you want with data
I suggest you go through the Methods of the classes to override them.FormView
Related
I've just started my first app with Django by following a video on YouTube.
The app is a students dashboard with 8 tools and features to help the student to make note, search for help, save books, etc.
When I follow the steps I get stuck in the creation of a new note but note from the admin side but from the actual notes template that has a crispy form and a create button.
The program is supposed for writing a title and description (the content of the note) and then press the create button. When I try to click, nothing is happening.
#This is the view.py page in the dashboard app :
from django.shortcuts import render
from . forms import *
from django.contrib import messages
# Create your views here.
def home(request):
return render(request, 'dashboard/home.html')
def notes(request):
if request.method == "POST":
form = NotesForm(request.POST)
if form.is_valid():
notes = Notes(user=request.user,title=request.POST['title'],description=request.POST['description'])
notes.save()
messages.success(request,f"Notes Added from {request.user.username} Successfully")
else:
form = NotesForm()
notes = Notes.objects.filter(user=request.user)
context = {'notes':notes,'form':form}
return render(request,'dashboard/notes.html',context)
and this is the notes.html page in the dashboard app > template folder > dashboard folder :
{% extends 'dashboard/base.html' %}
<!-- Load the static files here -->
{% load static %} {% load crispy_forms_tags %}
<!-- loading the crispy files must be here so now i write anything to
create some space and lines here we go baby lets add more -->
{% block content %}
<div class="container">
<div class="row">
{% for note in notes %}
<div class="col-md-3">
<a href="#">
<div class="card">
<div class="card-header">{{note.title}}</div>
<div class="card-body">{{note.description|slice:"0:100"}}</div>
<div class="card-footer mt-auto">
<i class="fa fa-trash fa-2x"></i>
</div>
</div>
</a>
</div>
{% endfor %}
<br /><br />
</div>
</div>
<br /><br />
<div class="container">
<form method="POST">
{% csrf_token %}
<fieldset class="form-group">
<legend class="border-bottom mb-4">Create Notes</legend>
</fieldset>
{% crispy form %}
<div class="form-group">
<button href="" class="btn btn-outline-info" type="submit">Create</button>
</div>
</form>
</div>
{% endblock content %}
so, this the models.py file too
from django.db import models
from django.contrib.auth.models import User
# Create your models here.
class Notes(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE)
title = models.CharField(max_length=200)
description = models.TextField()
def __str__(self):
return self.title
class Meta:
verbose_name = "notes"
verbose_name_plural = "notes"
in case the problem is not here, please take a look on the inside this forms.py
from dataclasses import fields
from django import forms
from . models import *
class NotesForm(forms.ModelForm):
class Meta:
model = Notes
fields = ['title', 'description']
and .. urls.py
from django.urls import path
from . import views
urlpatterns = [
path('',views.home, name='home'),
path('notes',views.notes, name="notes")
]
There are many minor mistakes in the code:
It is {{form|crispy}} to display the form not {% crispy form %}.
You are also not using HttpResponseRedirect[django-doc] for redirecting, after saving form from POST data, you should always return an HttpResponse, its a good practice.
Try below code:
Notes.html
{% block content %}
{% load crispy_forms_tags %}
<div class="container">
<div class="row">
{% for note in notes %}
<div class="col-md-3">
<a href="#">
<div class="card">
<div class="card-header">{{note.title}}</div>
<div class="card-body">{{note.description|slice:"0:100"}}</div>
<div class="card-footer mt-auto">
<i class="fa fa-trash fa-2x"></i>
</div>
</div>
</a>
</div>
{% endfor %}
<br /><br />
</div>
</div>
<br /><br />
<div class="container">
<form method="POST" action="{% url 'notes' %}" novalidate>
{% csrf_token %}
<fieldset class="form-group">
<legend class="border-bottom mb-4">Create Notes</legend>
</fieldset>
{{form|crispy}}
<input type="submit" value="Create">
</form>
</div>
{% endblock content %}
views.py
from django.http import HttpResponseRedirect
from django.shortcuts import render
from django.shortcuts import render
from . forms import *
from django.contrib import messages
from django.urls import reverse
def home(request):
return render(request, 'dashboard/home.html')
def notes(request):
if request.method == "POST":
form = NotesForm(request.POST)
if form.is_valid():
title = form.cleaned_data['title']
descrip = form.cleaned_data['description']
notes = Notes(
user=request.user, title=title, description=descrip)
notes.save()
messages.success(
request, f"Notes Added from {request.user.username} Successfully")
return HttpResponseRedirect(reverse('thanks'))
else:
form = NotesForm()
notes = Notes.objects.filter(user=request.user)
context = {'notes': notes, 'form': form}
return render(request, 'dashboard/notes.html', context)
def thanks(request):
return render(request, 'dashboard/thanks.html')
urls.py
from django.urls import path
from . import views
urlpatterns = [
path('', views.home, name='home'),
path('notes/', views.notes, name="notes"),
path('thanks/', views.thanks, name='thanks')
]
thanks.html
<body>
<h2>The form successfully submitted</h2>
</body>
Your forms.py can be remain same as it is.
I am trying to create a comment creation page related to a single post on a blog-style site using Django.
The home page has a list of posts, each with a "comment" button. Ideally this "comment" button would then take you to a page that would have the original post listed at the top with the comment creation form underneath it.
I've been trying to figure out a way to access the data I'm looking for using primary keys but am not sure how to tie everything together.
Here are the 2 models I am trying to access (Post and Comment):
class Post(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE, null=True)
title = models.CharField(max_length=128)
content = models.TextField()
image = models.ImageField(upload_to='post_pics', blank=True)
date_posted = models.DateTimeField(default=timezone.now)
def get_absolute_url(self):
return reverse('home')
class Comment(models.Model):
post = models.ForeignKey(Post, on_delete=models.CASCADE, related_name='comments')
user = models.ForeignKey(User, on_delete=models.CASCADE)
comment = models.TextField()
date_created = models.DateTimeField(default=timezone.now)
And the urls.py:
from django.urls import path
from .views import PostCreateView, PostListView, VehicleListView, CommentCreateView
from . import views
urlpatterns = [
path('', PostListView.as_view(), name='home'),
path('comment/new/<int:pk>', CommentCreateView.as_view(), name='comment-create'),
]
This is my current HTML for the home page (currently adds the post id to the end of the HTML on linked "comment-create" page):
{% for post in posts %}
<div class="infinite-item">
<div class="card m-3">
<div class="card-body" style="background-color:#bdcade; padding-bottom:0px">
<div class="media mb-3">
<img src="{{ user.profile.image.url }}" class="d-block ui-w-40 rounded-circle" style="width:40px;height:auto;" alt="">
<div class="media-body ml-3">
<h5 style="color:#ffffff">{{ post.user.username }}</h5>
<div class="small text-muted">Yesterday</div>
</div>
</div>
</div>
<div class="card-body">
<h5 class="card-title">{{ post.title }}</h5>
<p class="card-text">{{ post.content }}</p>
<div class="btn-group" role="group">
<button type="button" class="btn btn-secondary">Like</button>
<a class="btn btn-secondary" href="{% url 'comment-create' post.id %}" role="button">Comment</a>
</div>
</div>
</div>
</div>
This is my current HTML for the comment-creation page:
{% block content %}
<div class="card m-3">
<div class="card-body" style="background-color:#bdcade; padding-bottom:0px">
<div class="media mb-3">
<img src="{{ post.user.profile.image.url }}" class="d-block ui-w-40 rounded-circle" style="width:40px;height:auto;" alt="">
<div class="media-body ml-3">
<h5 style="color:#ffffff">{{ post.user.username }}</h5>
<div class="small text-muted">{{ post.title }}</div>
</div>
</div>
</div>
<div class="card-body">
<h5 class="card-title">{{ post.title }}</h5>
<p class="card-text">{{ post.content }}</p>
<div class="btn-group" role="group">
<button type="button" class="btn btn-secondary">Like</button>
<button type="button" class="btn btn-secondary">Comment</button>
</div>
</div>
</div>
<br>
<div class="container primary-segments">
<form method="POST" enctype="multipart/form-data">
{% csrf_token %}
<fieldset>
{{ form|crispy }}
</fieldset>
<div class="form-group">
<button class="btn btn-outline-info" type="submit">Post Comment</button>
</div>
</form>
</div>
{% endblock content %}
Here is the view for comment creation page (using get_context_data to access the Post model):
class CommentCreateView(LoginRequiredMixin, CreateView):
model = Comment
template_name = 'home/comment-form.html'
fields = ['comment',]
def get_context_data(self, **kwargs):
context = super(CommentCreateView, self).get_context_data(**kwargs)
pk = self.kwargs['pk']
context['post'] = Post.objects.filter(id=pk)
return context
def form_valid(self, form):
form.instance.user = self.request.user
return super().form_valid(form)
Currently, I can't access any of the data for the post in the comment creation page. I think it has to do with how I'm trying to tie the pk to the post within the -get_context_data` function. The primary key for the desired post is showing up in the URL, just not sure how to get at the right data.
Any help would be much appreciated. Thank you!
So it looks to me like you're using pk wrong here.
The pk in this code segment here:
def get_context_data(self, **kwargs):
context = super(CommentCreateView, self).get_context_data(**kwargs)
pk = self.kwargs['pk']
is the pk of the comment, however you're trying to use it to lookup a Post with context['post'] = Post.objects.filter(id=pk). Remember that pk stands for primary key and references the object that you declared as the model for your view. If you want the Post associated with that Comment you'll need to search for it using Comment.post since that's the declared foreign key inside your model.
After a lot of Googling/trial-and-error, I found a solution that got me what I was looking for.
Updated my View from THIS (note: I was trying to force a specific PK to test if it would filter to the correct data, but for some reason I still couldn't access anything):
class CommentCreateView(LoginRequiredMixin, CreateView):
model = Comment
template_name = 'home/comment-form.html'
fields = ['comment',]
def get_context_data(self, **kwargs):
context = super(CommentCreateView, self).get_context_data(**kwargs)
pk = 7
context['post'] = Post.objects.filter(id=pk)
return context
def form_valid(self, form):
form.instance.user = self.request.user
return super().form_valid(form)
To THIS:
class CommentCreateView(LoginRequiredMixin, CreateView):
model = Comment
template_name = 'home/comment-form.html'
fields = ['comment',]
def get_context_data(self, **kwargs):
context = super(CommentCreateView, self).get_context_data(**kwargs)
context['post'] = Post.objects.get(pk=self.kwargs['pk'])
return context
def form_valid(self, form):
form.instance.user = self.request.user
return super().form_valid(form)
Have a basic, high-level understanding of what this is doing (grabbing the PK from the URL), but am not entirely sure why the original .filter() method wasn't working.
If anyone could provide some context as to what's going on behind the scenes here, I'd love to see it.
Thanks!
I'm making a Post and Comment model by taking reference from internet. i created and Post and Comment model and it looks ok in django admin panel. i can add post and also a comment to any particular post. but getting trouble when I'm trying to display the comment under the post in templates(under post detail views). PLEASE HELP
models.py
class Post(models.Model):
author = models.ForeignKey(User,on_delete=models.CASCADE)
title = models.CharField(max_length=100)
content = RichTextField()
tags = models.CharField(max_length=50,blank=True,null=True)
date_posted = models.DateTimeField(default=timezone.now)
def __str__(self):
return self.title
def get_absolute_url(self):
return reverse('post-detail',kwargs={'pk':self.pk})
class Comment(models.Model):
post = models.ForeignKey(Post,on_delete=models.CASCADE)
author = models.ForeignKey(User,max_length=50,on_delete=models.CASCADE)
text = models.TextField()
create_date = models.DateTimeField(default=timezone.now)
def get_absolute_url(self):
return reverse('discuss')
views.py
class PostDetailView(DetailView):
model = Post
def add_comment_to_post(request,pk):
return get_object_or_404(Post,pk=pk)
if request.method == "POST":
form = CommentForm(request.POST)
if form.is_valid():
comment = form.save(commit=False)
comment.post= post
comment.save()
return redirect('post-detail',pk=post.pk)
else:
form = CommentForm()
return render(request, 'discuss/comment_form.html',{'form':form})
def comment_remove(request,pk):
comment = get_object_or_404(Comment,pk=pk)
post_pk = comment.post.pk
comment.delete()
return redirect('post-detail', pk=post_pk)
post_detail.html
{% extends 'index.html' %}
{% block content %}
<article class="media content-section">
<div class="medaia-body">
<img class="rounded-circle article-img" src="{{ object.author.profile.image.url }}" alt="image not found">
<div class="article-metedata">
<a class="mr-2" href="{% url 'user-posts' object.author.username %}">{{object.author}}</a>
<small class="text-muted">{{ object.date_posted|date:"F d, Y"}}</small>
</div>
<h2 class="article-title">{{ object.title }}</h2>
<img class="query-img" src="{{ object.image.url }}" alt="image not found">
<p class="article-content">{{ object.content|safe }}</p>
</div>
</article>
{% if object.author == user %}
<div class="post-update-delete">
<button class="btn btn-outline-primary">Edit Post</button>
<button class="btn btn-outline-primary">Delete Post</button>
</div>
{% endif %}
<hr>
<a class="btn btn-primary btn-comment" href="{% url 'add_comment_to_post' pk=post.pk %}">Add Comment</a>
<!-- ############################### ABOVE CODE IS WORKING ############################# -->
<!-- ########################## GETTING PROBLEM IN BELLOW CODE ######################### -->
{% for comment in object.comments.all %}
{% if user.is_authenticated %}
{{ comment.create_date }}
{{ comment.text|safe|linebreaks }}
{{ comment.author }}
{% endif %}
{% empty %}
<p>No Comment</p>
{% endfor %}
{% endblock %}
in post_deatil.html i also tried {% for comment in post.comments.all %} but it is also not working
Since you did not specify a related_name=… parameter [Django-doc], the related_name is by default comment_set, so you iterate over the comments with:
{% for comment in object.comment_set.all %}
…
{% endfor %}
Note: It is normally better to make use of the settings.AUTH_USER_MODEL [Django-doc] to refer to the user model, than to use the User model [Django-doc] directly. For more information you can see the referencing the User model section of the documentation.
I am working on invoice management system having in which user can add invoice data and it will save in database and whenever user logged in the data will appear on home page.but the problem if whenever any user logged in the home page showing other users data also but i want active users data only. could you guys help me.
here is my home page,you can see i am logged in through user 1 but at home page user 2 data also appear
here is my code
views.py
from django.shortcuts import render
from django.contrib.auth.mixins import LoginRequiredMixin, UserPassesTestMixin
from django.views.generic import (
ListView,
DetailView,
CreateView,
UpdateView,
DeleteView
)
from .models import Invoicelist
def home(request):
context = {
'invoices': Invoicelist.objects.all()
}
return render(request, 'invoicedata/home.html', context)
class InvoiceListView(ListView):
model = Invoicelist
template_name = 'invoicedata/home.html' # <app>/<model>_<viewtype>.html
context_object_name = 'invoices'
class InvoiceDetailView(DetailView):
model = Invoicelist
class InvoiceCreateView(LoginRequiredMixin, CreateView):
model = Invoicelist
fields = ['issuer','invoice_number','date','amount','currency','other']
def form_valid(self, form):
form.instance.author = self.request.user
return super().form_valid(form)
class InvoiceUpdateView(LoginRequiredMixin, UserPassesTestMixin, UpdateView):
model = Invoicelist
fields = ['issuer','invoice_number','date','amount','currency','other']
def form_valid(self, form):
form.instance.author = self.request.user
return super().form_valid(form)
def test_func(self):
invoice = self.get_object()
if self.request.user == invoice.author:
return True
return False
class InvoiceDeleteView(LoginRequiredMixin, UserPassesTestMixin, DeleteView):
model = Invoicelist
success_url = '/'
def test_func(self):
invoice = self.get_object()
if self.request.user == invoice.author:
return True
return False
def about(request):
return render(request, 'invoicedata/about.html', {'title': 'About'})
models.py
from django.db import models
from django.contrib.auth.models import User
from django.utils import timezone
from django.urls import reverse
class Invoicelist(models.Model):
issuer = models.CharField(max_length=50)
invoice_number = models.CharField(max_length=50)
date = models.CharField(max_length=50)
amount = models.IntegerField()
currency = models.CharField(max_length=10)
other = models.TextField()
author = models.ForeignKey(User, on_delete=models.CASCADE)
def __str__(self):
return (self.issuer)
def get_absolute_url(self):
return reverse('invoice-detail', kwargs={'pk': self.pk})
home.html
{% extends "invoicedata/base.html" %}
{% block content %}
{% for invoice in invoices %}
<article class="media content-section">
<div class="media-body">
<div class="article-metadata">
<h2><a class="article-title" href="{% url 'invoice-detail' invoice.id %}">{{ invoice.issuer }}</a></h2>
</div>
<p class="article-content">{{ invoice.invoice_number }}</p>
<p class="article-content">{{ invoice.date }}</p>
<p class="article-content">{{ invoice.amount }}</p>
<p class="article-content">{{ invoice.currency }}</p>
<p class="article-content">{{ invoice.other }}</p>
<div class="article-metadata">
<small class="text-muted">{{ invoice.author }}</small>
</div>
</div>
</article>
{% endfor %}
{% endblock content %}
invoicelist_deatil.html
{% extends "invoicedata/base.html" %}
{% block content %}
<article class="media content-section">
<img class="rounded-circle article-img" src="{{ object.author.profile.image.url }}">
<div class="media-body">
<div class="article-metadata">
<a class="mr-2" href="#">{{ object.author }}</a>
<small class="text-muted">{{ object.date }}</small>
{% if object.author == user %}
<div>
<a class="btn btn-secondary btn-sm mt-1 mb-1" href="{% url 'invoice-update' object.id %}">Update</a>
<a class="btn btn-danger btn-sm mt-1 mb-1" href="{% url 'invoice-delete' object.id %}">Delete</a>
</div>
{% endif %}
</div>
<h2 class="article-issuer">{{ object.issuer }}</h2>
<p class="article-invoice_number">{{ object.invoice_number }}</p>
<p class="article-date">{{ object.date }}</p>
<p class="article-amount">{{ object.amount }}</p>
<p class="article-currency">{{ object.currency }}</p>
<p class="article-other">{{ object.other }}</p>
</div>
</article>
{% endblock content %}
You'll have to override get_queryset() on e.g. the list view to only include the desired user's invoices. (You'll want to do this on the update view too, to avoid users being able to edit each others' invoices.)
As an aside, your model should be called Invoice, unless a single instance of the model actually represents a list of invoices.
class InvoiceListView(ListView):
model = Invoicelist
template_name = 'invoicedata/home.html'
context_object_name = 'invoices'
def get_queryset(self):
return self.model.objects.all().filter(issuer=self.request.user)
Nothing is happened when I put comment button.I wanna make a page which is shown comment&recomment.I wrote codes in views.py
class DetailView(generic.DetailView):
model = POST
template_name = 'detail.html'
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['comment_form'] = CommentCreateForm()
context['recomment_form'] = ReCommentCreateForm()
return context
class CommentCreateView(generic.View):
def post(self, request, *args, **kwargs):
form = CommentCreateForm(request.POST)
post = POST.objects.get(pk=kwargs['post_pk'])
if form.is_valid():
obj = form.save(commit=False)
obj.target = post
obj.save()
return redirect('detail', pk=post.pk)
class ReCommentCreateView(generic.View):
def post(self, request, *args, **kwargs):
form = ReCommentCreateForm(request.POST)
comment = Comment.objects.get(pk=kwargs['comment_pk'])
if form.is_valid():
obj = form.save(commit=False)
obj.target = comment
obj.save()
return redirect('detail', pk=comment.target.pk)
in urls.py
from django.urls import path
from django.conf import settings
from . import views
urlpatterns = [
path('detail/<int:pk>/', views.DetailView.as_view(), name='detail'),
path('comment/<int:post_pk>/',views.CommentCreateView.as_view(), name='comment'),
path('recomment/<int:comment_pk>/', views.ReCommentCreateView.as_view(), name='recomment'),
]
in detail.html
{% load static %}
<html lang="en">
<head>
<meta charset="UTF-8">
<title>DETAIL</title>
</head>
<body>
<div id="comment-area">
{% for comment in post.comment.all %}
<div class="media m-3">
<div class="media-body">
<h5 class="mt-0">
<span class="badge badge-primary badge-pill">{% by_the_time comment.created_at %}</span>
{{ comment.name }}
<span class="lead text-muted">{{ comment.created_at }}</span>
Recomment
</h5>
{{ comment.text | linebreaksbr }}
{% for recomment in comment.recomment.all %}
<div class="media m-3">
<div class="media-body">
<h5 class="mt-0">
{{ recomment.name }}
<span class="lead text-muted">{{ recomment.created_at }}</span>
</h5>
{{ recomment.text | linebreaksbr }}
</div>
</div>
{% endfor %}
<form action="{% url 'recomment' comment_pk=comment.id %}" method="post">
{{recomment_form}}
{% csrf_token %}
<input class="btn btn-primary" type="submit" value="re-comment">
</form>
</div>
</div>
{% endfor %}
<form action="{% url 'comment' post_pk=post.id %}" method="post">
{{comment_form}}
{% csrf_token %}
<input class="btn btn-primary" type="submit" value="comment">
</form>
</div>
</body>
</html>
in models.py
class Comment(models.Model):
name = models.CharField(max_length=100, blank=True)
text = models.TextField()
target = models.ForeignKey(POST, on_delete=models.CASCADE)
created_at = models.DateTimeField(auto_now_add=True)
def __str__(self):
return self.name
When I put comment button, no error happens but same page which is form's content is blank is shown.I wanna show {% for comment in post.comment.all %} ~ {% endfor %},so I really cannot understand why I cannot do it.I think values are not in model, so I print out print(obj) in CommentCreateView's class, and correctly value is shown in terminal.What is wrong in my code?How should I fix this?
Do you have the objects actually created in the DB? There's some info missing in the post (like the Form itself, etc), anyway:
If you didn't set the related_name argument on the foreign key, the correct access to post's comments is via post.comment_set.all(), see:
https://docs.djangoproject.com/en/2.0/topics/db/queries/#following-relationships-backward
May I point out that you're doing a lot of work, that Django's generic views can do for you (guess it's OK if you're learning, otherwise, it's just more code to maintain).
Things like CreateView, UpdateView, and DeleteView from django.views.generic.edit. For more info see:
https://docs.djangoproject.com/en/2.0/topics/class-based-views/generic-editing/#model-forms