Generic Class: UpdateView and DeleteView not saving data back to model - django

The UpdateView and DeleteView not saving data back to model
views.py
class ProjectList(ListView):
model = Project
template_name = 'mainapp/browse_app.html'
context_object_name = 'projs'
class ProjectUpdate(UpdateView):
model = Project
fields = ['pname','desc','emailID']
template_name = 'mainapp/project_form_edit.html'
class ProjectDelete(DeleteView):
model = Project
fields = ['id','pname','desc','emailID','updated_on']
template_name = 'mainapp/index.html'
success_url = reverse_lazy('mainapp/projs')
def form_display(request):
data = Project.objects.all()
return render(request,'mainapp/browse_page.html',{'data':data})
...
browse_page.html: has an edit link and a delete button and it displays the project details of the project which is clicked
{% for i in data %}
<center>
{{ i }}
</center>
<!-- Modal -->
<div id="costumModal13{{ forloop.counter }}" class="modal" data-easein="bounceLeftIn" tabindex="-1" role="dialog" aria-labelledby="costumModalLabel" aria-hidden="true">
<a class="btn btn-info btn-lg fa fa-pencil-square-o" href="{% url 'project_edit' pk=i.id %}" aria-hidden="true">Edit</a>
<form method="POST" action="{% url 'project_del' pk=i.id %}">
{% csrf_token %}<input type="submit" value="Delete">
</form>
{{ i.pname }}
{{ i.id }}
{{ i.updated_on }}
</div>
{% endfor %}
urls.py
from django.contrib import admin
from django.urls import path, include, re_path
from mainapp import views
from mainapp.views import ProjectUpdate, ProjectDelete
app_name = 'mainapp'
urlpatterns = [
path('browse/',views.form_display,name="browse_page"),
re_path(r'^browse/(?P<pk>\d+)/$', ProjectUpdate.as_view(), name='project_edit'),
re_path(r'^browse/delete/(?P<pk>\d+)/$', ProjectDelete.as_view(), name='project_del'),
]
On submitting the edited form:
On clicking on delete button:
Can you help me resolve these 2 errors?

1st image problem:
The url has index.html appended to the end. Your url is defined as /browse/7/
2nd image problem:
The namespace delimeter is : not /.
class ProjectDelete(DeleteView):
...
success_url = reverse_lazy('mainapp:projs')

Related

Django upload multiple images with one input Method Not Allowed (POST): /images/

HI I have just built an HTML-form in which the user can upload multiple images but I am getting Method Not Allowed (POST): /images/ all the time. In the image_list.html the images should be shown.
Thanks for your help
models.py
class ImageModel(models.Model):
images = models.ImageField(upload_to='products/')
forms.py
class ImageForm(ModelForm):
class Meta:
model = ImageModel
fields = ['images']
widgets = {
'images': FileInput(attrs={'multiple': True}),
}
views.py
class ImageFormView(FormMixin, TemplateView):
template_name = 'image_form.html'
form_class = ImageForm
def form_valid(self, form):
# Save the images to the database
form.save()
# Redirect to the image list view
return redirect('image_list')
class ImageListView(ListView):
model = ImageModel
template_name = 'image_list.html'
context_object_name = 'images'
image_form.html
<form method="post" enctype="multipart/form-data">
{% csrf_token %}
{{ form.as_p }}
<button type="submit" class="btn btn-primary">Submit
</form>
image_list.html
{% load static %}
{% for image in images %}
<img src="{{image.images.url}}" alt="">
{% endfor %}
urls.py
urlpatterns = [
path('images/', views.ImageFormView.as_view(), name='image_form'),
path('', views.ImageListView.as_view(), name='image_list'),
]
You need to close button tag at HTML form
<form method="POST" enctype="multipart/form-data">
{% csrf_token %}
{{ form.as_p }}
<button type="submit" class="btn btn-primary">Submit</button>
</form>

No User matches a given query Django

While trying to display post-detail template in a blog app, I get an error No User matches a given query. In my models I am inheriting from User model. What seems the problem?
# Third party imports.
from django.contrib.auth.models import User
from django.db import models
from django.urls import reverse
from django.utils import timezone
class Post(models.Model):
"""
Creates Model for a post in the database.
"""
title = models.CharField(max_length=100)
sub_title = models.CharField(max_length=100)
content = models.TextField()
date_posted = models.DateTimeField(default=timezone.now)
author = models.ForeignKey(User, on_delete=models.CASCADE)
def __str__(self):
"""
A string representation of the post model.
"""
return self.title
def get_absolute_url(self):
"""
Creates the absolute url for a particular post.
"""
return reverse('blog:post-detail', kwargs={'pk': self.pk})
views.py
class PostDetailView(DetailView):
"""
View for a post's details
"""
model = Post
template_name ='blog/post_detail.html'
context_object_name = 'posts'
paginate_by = 3
def get_queryset(self):
user = get_object_or_404(User, username=self.kwargs.get('username'))
return Post.objects.filter(author=user).order_by('-date_posted')
urls.py:
# Third party imports.
from django.urls import path
from .views import (
PostListView,
PostDetailView,
PostCreateView,
PostUpdateView,
PostDeleteView,
UserPostListView,
)
# Local application imports.
from . import views
# Specifies the app's name.
app_name = "blog"
urlpatterns = [
path('', PostListView.as_view(), name='home'), # url for post list.
path('user/<str:username>/',
UserPostListView.as_view(),
name='user-posts'), # url for specific user post.
path('post/<int:pk>/',
PostDetailView.as_view(),
name='post-detail'), # url for post detail.
path('post/new/',
PostCreateView.as_view(),
name='post-create'), # url to create new post.
path('post/<int:pk>/update/',
PostUpdateView.as_view(),
name='post-update'), # url to update post.
path('post/<int:pk>/delete/',
PostDeleteView.as_view(), name='post-delete'), # url to delete post.
path('about/', views.about, name='about'), # url for about page.
]
home.html
{% extends 'blog/base.html' %}
{% load static %}
{% block crumb %}
{% for post in posts %}
<div class="post-preview">
<a href="{% url 'blog:post-detail' post.id %}">
<h2 class="post-title">{{ post.title }} </h2>
<h3 class="post-subtitle">{{ post.sub_title }} </h3>
</a>
<p class="post-meta" style="font-weight: 300;"> Posted by
<a class="text-muted">{{post.author}} on {{ post.date_posted|date:"F d, Y" }}</a>
</p>
</div>
<hr class="my-4" />
{% endfor %}
{% endblock %}
post_detail.html
{% extends "blog/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="{% url 'blog:user-posts' object.author.username %}">{{ object.author }}
</a>
<small class="text-muted">{{ object.date_posted|date:"F d, Y" }}</small>
{% if object.author == user %}
<div>
<a class="btn btn-secondary btn-sm mt-1 mb-1"
href="{% url 'blog:post-update' object.id %}">Update
</a>
<a class="btn btn-danger btn-sm mt-1 mb-1"
href="{% url 'blog:post-delete' object.id %}">Delete
</a>
</div>
{% endif %}
</div>
<h2 class="article-title">{{ object.title }}</h2>
<p class="article-content">{{ object.content }}</p>
</div>
</article>
{% endblock content %}
Some notes about things that are wrong that aren't directly related to the error you're getting.
You're using a DetailView (which shows the details for a single object) where it looks like you want a ListView (to list an user's posts).
Your context_object_name is in plural, which implies you do want a list of things.
paginate_by has no effect in DetailViews, since a single-object view couldn't be paginated.
Your view refers to a blog/post_detail.html, while you've pasted in a home.html. Which one is it?
You say "In my models I am inheriting from User model.", but it looks like you're not importing a custom User model, but the default from django.contrib.auth.models import User. If you really are using a custom user model that inherits from BaseUser (not User), then that's wrong too.
The error itself, I imagine (since you're not supplying us with the traceback), comes from
user = get_object_or_404(User, username=self.kwargs.get('username'))
which means you're not passing in an <username> in the URL for that view, or the <username> you're using is incorrect (and there is no such user). (You're not showing us your urls.py, so that's just a guess.)
(A Detail view's URL, in any case, would probably not refer to an username, but the unique identifier of the post.)
EDIT
Following the edit of the original post, the issue is that the get_queryset() is simply extraneous (since no filtering by username is required) in that DetailView, so the class can be simplified to:
class PostDetailView(DetailView):
model = Post
template_name ='blog/post_detail.html'

update an object without DetailView Django

I'm looking for a solution to update an object without having to go to the detail page but, just edit it on the page itself. What I want to achieve is when I click on edit: the object becomes a field where I can edit and save it. All the YouTube tutorials show the edit->detail page version.
So a quick/direct edit on the object itself that is on the homepage without leaving the homepage.
I have tried to use the UpdateView on this but then there is separate HTML file necessary, which would result in leaving the homepage. I would like to get some help or tips on this.
urls.py
from django.urls import path
from .views import (
HomePageView,
TaskCreateView,
TaskDeleteView,
TaskUpdateView,
)
urlpatterns = [
path('', HomePageView.as_view(), name='home'),
path('task_new/', TaskCreateView.as_view(), name='task_new'),
path('<int:pk>/task_delete/', TaskDeleteView.as_view(), name='task_delete'),
path('<int:pk>/task_edit/', TaskUpdateView.as_view(), name='task_edit'),
]
views.py
from django.views.generic import ListView
from django.views.generic.edit import CreateView, DeleteView, UpdateView
from django.urls import reverse_lazy
from .models import Task
class HomePageView(ListView):
model = Task
template_name = 'home.html'
context_object_name = 'all_tasks_list'
class TaskCreateView(CreateView):
model = Task
fields = ['text',]
class TaskDeleteView(DeleteView):
model = Task
success_url = reverse_lazy('home')
class TaskUpdateView(UpdateView):
model = Task
fields = ['text',]
home.html
<!DOCTYPE html>
<html>
<head>
<title>Todo app</title>
</head>
<body>
<h1>Todo app</h1>
<ul>
{% for task in all_tasks_list %}
<li>{{ task.text }}</li>
<form action="{% url 'task_delete' task.pk %}" method="post">{% csrf_token %}
<input type="submit" value="Delete"/></form>
<form action="{% url 'task_edit' task.pk %}" method="post">{% csrf_token %}
<input type="submit" value="Edit"/>
</form>
{% endfor %}
</ul>
<form action="{% url 'task_new' %}" method="post">{% csrf_token %}
<input type="text" name="text"/>
<input type="submit" value="Add"/>
</form>
</body>
</html>
models.py
from django.db import models
from django.urls import reverse
class Task(models.Model):
text = models.TextField()
def __str__(self):
return self.text[:50]
def get_absolute_url(self):
return reverse('home')
The error says task_form.html doesn´t exist, default template for an UpdateView. Are you sure it exists? If you want to use another template you have to specify
class TaskUpdateView(UpdateView):
model = Task
fields = ['text',]
template_name = 'todoapp/my_template.html'
Considering you are using generic view you should follow documentation for UpdateView
In this particular case you are missing form that UpdateView looks for to render GET request
The UpdateView page displayed to a GET request uses a
template_name_suffix of '_form'.
In your particular case it is todoapp/task_form.html which you should create
<form method="post">{% csrf_token %}
{{ form.as_p }}
<input type="submit" value="Update"> </form>

Customising Django Crispy forms for formatted user input (formatted by the user)

In a Django project, I have used crispy forms to generate a form for a user to enter a title and a comment. For the comment section, I wish the user to be able to format their input (e.g add bold and italics tags, and also embed youtube videos, so use embed snippets). This is typical of a wordpress widget (admin) which allows text formatting for any text entry. Is it possible to do this with Django's Crispy forms, and if so, what is the best way forward? I am looking for suggestions for documentation, imports (any existing libraries compatible with Django?), as I as I couldn't find any, or an idea as to how to implement this manually. e.g. specifically, in which 'file' would it be implemented.
View of the html page (current)
View of the current form and text input on the comment area.
I've looked at the other questions-answers, but none answers this specifically.
This is the sort of functioanlity I would like to add to the form text input (for the content/comment box only)
Desired text input formatting options added
The current post_form.html code is below
{% extends "socialmedia/base.html" %}
{% load crispy_forms_tags %}
{% block content %}
<div class="content-section">
<form method="POST">
{% csrf_token %}
<fieldset class="form-group">
<legend class="border-bottom mb-4">Post your message</legend>
{{form|crispy}}
</fieldset>
<div class="form-group">
<button class="btn btn-outline-info" type="submit">Post</button>
</div>
</form>
</div>
{% endblock content %}
post_detail.html
{% extends "socialmedia/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="{% url 'user-posts' object.author.username %}">{{ object.author }}</a>
<small class="text-muted">{{ object.date_posted|date:"F d, Y"}}</small>
{% if object.author == user %}
<div>
<a class="btn btn-secondary btn-sm mt-1 mb-1" href="{% url 'post-update' object.id %}"> Update </a>
<a class="btn btn-danger btn-sm mt-1 mb-1" href="{% url 'post-delete' object.id %}"> Delete </a>
</div>
{% endif %}
</div>
<h2 class="article-title">{{ object.title }}</h2>
<p class="article-content">{{ object.content }}</p>
</div>
</article>
{% endblock content %}
models.py (relevant to the form)
from django.db import models
from django.utils import timezone #don't forget to add this
from django.contrib.auth.models import User
from django.urls import reverse
class Post(models.Model):
title=models.CharField(max_length=100)
content=models.TextField(max_length=300)
date_posted=models.DateTimeField(default=timezone.now)
views.py (again relating to the form)
from django.shortcuts import render,get_object_or_404
from django.contrib.auth.mixins import LoginRequiredMixin, UserPassesTestMixin
from django.contrib.auth.models import User
from .models import Post #import the Models Post
from django.views.generic import (
ListView,
DetailView,
CreateView,
UpdateView,
DeleteView
)
from django.http import HttpResponse
# Create your views here.
class PostListView(ListView):
model = Post #what model to query in order to create the list
template_name = 'socialmedia/home.html'
context_object_name = 'posts'
ordering = ['-date_posted']#the minus sign before the -date_posted makes it newest first
paginate_by = 10
class UserPostListView(ListView):
model = Post #what model to query in order to create the list
template_name = 'socialmedia/user_posts.html'
context_object_name = 'posts'
paginate_by = 10
def get_queryset(self):
user = get_object_or_404(User, username=self.kwargs.get('username'))
return Post.objects.filter(author=user).order_by('-date_posted')
class PostDetailView(DetailView):
model = Post
class PostCreateView(LoginRequiredMixin,CreateView):
model = Post
fields=['title','content']
def form_valid(self,form):
form.instance.author = self.request.user #set author to current loggged in user
return super().form_valid(form)
class PostUpdateView(LoginRequiredMixin,UserPassesTestMixin, UpdateView):
model = Post
fields=['title','content']
def form_valid(self,form):
form.instance.author = self.request.user
return super().form_valid(form)
#this only lets the user of the post update the post.....
def test_func(self):
post = self.get_object() #this gets the current post
if self.request.user == post.author:
return True
return False
class PostDeleteView(LoginRequiredMixin,UserPassesTestMixin, DeleteView):
model = Post
success_url = '/'
def test_func(self):
post = self.get_object() #this gets the current post
if self.request.user == post.author:
return True
return False

Django using generic views, error http 405

I am trying use the django generic view for CRUD, but the DeleteView result in error 405, following the guide official Django https://docs.djangoproject.com/en/2.1/ref/class-based-views/generic-editing/ , I don't understand where is my error, so that's my code:
views.py
from django.urls import reverse
from django.views.generic import (
CreateView,
DetailView,
ListView,
UpdateView,
ListView,
DeleteView
)
from .forms import ContatoModelForm
from .models import Contato
class ContatoCreateView(CreateView):
template_name = 'contato/contato_create.html'
form_class = ContatoModelForm
model = Contato
class ContatoListView(ListView):
template_name = 'contato/contato_list.html'
model = Contato
class ContatoDetailView(DetailView):
template_name = 'contato/contato_detail.html'
model = Contato
class ContatoUpdateView(UpdateView):
template_name = 'contato/contato_create.html'
form_class = ContatoModelForm
model = Contato
class ContatoDeleteView(DeleteView):
template_name = 'contato/contato_delete.html'
model = Contato
def get_success_url(self):
return reverse('contato:contato-list')
urls.py
from django.urls import path
from .views import (
ContatoCreateView,
ContatoDeleteView,
ContatoDetailView,
ContatoListView,
ContatoUpdateView,
)
app_name = 'contato'
urlpatterns = [
path('', ContatoListView.as_view(), name='contato-list'),
path('create/', ContatoCreateView.as_view(), name='contato-create'),
path('<int:pk>/', ContatoDetailView.as_view(), name='contato-detail'),
path('<int:pk>/update/', ContatoUpdateView.as_view(), name='contato-update'),
path('<int:pk>/delete/', ContatoDeleteView.as_view(), name='contato-delete'),
]
contato_delete.html
<form action='.' method='post'>{% csrf_token %}
<dialog class="mdl-dialog">
<h6>Deseja excluir {{ object.nome }}?</h6>
<div class="mdl-dialog__actions">
<input type="submit" class="mdl-button mdl-js-button mdl-button--accent" value="Excluir">
<button type="button" class="mdl-button close">Cancelar</button>
</div>
</dialog>
</form>
contato_detail.html
{% extends 'base.html' %}
{% load staticfiles %}
{% block content %}
<div class="mdl-card__title">
<h2 class="mdl-card__title-text">{{ object.nome }}</h2>
</div>
<div class="mdl-layout-spacer"></div>
<a id="show-dialog" class="mdl-button mdl-js-button mdl-button--icon">
<i class="material-icons">delete</i>
</a>
{% include "contato/contato_delete.html" %}
<script type="text/javascript" src="{% static 'js/dialog.js' %}"></script>
<div class="mdl-card__actions mdl-card--border">
<ul class="demo-list-icon mdl-list">
<li class="mdl-list__item">
<span class="mdl-list__item-primary-content">
<i class="material-icons mdl-list__item-icon">call</i>
{{ object.celular }}
</span>
</li>
<li class="mdl-list__item">
<span class="mdl-list__item-primary-content">
<i class="material-icons mdl-list__item-icon">email</i>
{{ object.email }}
</span>
</li>
<li class="mdl-list__item">
<span class="mdl-list__item-primary-content">
<i class="material-icons mdl-list__item-icon">business</i>
{{ object.cargo }} na {{ object.empresa}}
</span>
</li>
</ul>
</div>
<a href="{% url 'contato:contato-update' object.id %}" id="fab" class="mdl-button mdl-js-button mdl-button--fab mdl-button--primary">
<i class="material-icons">create</i>
</a>
{% endblock %}
contato_list.html
{% extends "base.html" %}
{% block content %}
<style>
.demo-list-three {
width: 800px;
margin-left: 30px;
}
#fab {
position: fixed;
display: block;
right: 0;
bottom: 0;
margin-right: 40px;
margin-bottom: 40px;
z-index: 900;
}
</style>
<form method='GET' action=''>
</form>
{% for obj in object_list %}
<ul class="demo-list-three mdl-list">
<li class="mdl-list__item mdl-list__item--three-line">
<span class="mdl-list__item-primary-content">
<i class="material-icons mdl-list__item-avatar">person</i>
<a href='{{ obj.get_absolute_url }}'><span>{{ obj.nome }}</span>
</a>
<span class="mdl-list__item-text-body">
{{ obj.celular }} <br>
{{ obj.email }}
</span>
</span>
<a href="{{ obj.get_absolute_url }}" class="mdl-button mdl-js-button mdl-button--primary">
DETALHES
</a>
</li>
</ul>
{% endfor %}
<a href="{% url 'contato:contato-create' %}" id="fab" class="mdl-button mdl-js-button mdl-button--fab mdl-button--primary">
<i class="material-icons">person_add</i>
</a>
{% endblock content %}
Simplify your code:
class ContatoCreateView(CreateView):
template_name = 'contato/contato_create.html'
form_class = ContatoModelForm
model = Contato # Tell the CBV what object you will create
#** This doesn't make any sense, you are creating a new Contato not retrieving a queryset.
# queryset = Contato.objects.all()
#** Let the ModelForm handle the validation
# def form_valid(self, form):
# print(form.cleaned_data)
# return super().form_valid(form)
class ContatoListView(ListView):
template_name = 'contato/contato_list.html'
model = Contato # This does the work for you
#** You don't need to do this, unless you want a _specific_ queryset filterd
# queryset = Contato.objects.all()
class ContatoDetailView(DetailView):
template_name = 'contato/contato_detail.html'
model = Contato # again, just define the model
#** let CBV handle this logic
# def get_object(self):
# id_ = self.kwargs.get("id")
# return get_object_or_404(Contato, id=id_)
class ContatoUpdateView(UpdateView):
template_name = 'contato/contato_create.html'
form_class = ContatoModelForm
model = Contato # again, just define the model
#** let CBV handle this logic
# def get_object(self):
# id_ = self.kwargs.get("id")
# return get_object_or_404(Contato, id=id_)
#** Let the ModelForm handle the validation
#def form_valid(self, form):
# print(form.cleaned_data)
# return super().form_valid(form)
class ContatoDeleteView(DeleteView):
template_name = 'contato/contato_delete.html'
model = Contato # the class object you want to delete
#** let CBV handle this logic
# def get_object(self):
# id_ = self.kwargs.get("id")
# return get_object_or_404(Contato, id=id_)
def get_success_url(self):
return reverse('contato:contato-list')
You need to set the action on the form to match the route (url) for deleting a contato object.
Basically change the open tag of the <form> to:
<form action='{{ id }}/delete' method='post'>
and pass an id to the view through the context, like this:
class ContatoDetailView(DetailView):
model = Contato
def get(self, request, *args, **kwargs):
self.object = self.get_object()
context = self.get_context_data(object=self.object)
context['id'] = self.object.id
return self.render_to_response(context)
As an additional recommendation, I'd look into making your routes more RESTful. See this gist for info: https://gist.github.com/alexpchin/09939db6f81d654af06b. Something to note though is that HTML5 forms do not have support for delete (or put) requests. To make a delete request with a form you'd have to submit the request manually on submit using javascript.