I have a problem in implementing DeleteView for a model that has a related model. When I try to delete a Task object nothing happens and it is not redirecting to the success_url. Noting happens. It just keeps on displaying the template.
here are the models:
class Project(models.Model):
name = models.CharField(max_length=100)
description = models.TextField()
date_created = models.DateTimeField(default=timezone.now)
author = models.ForeignKey(User, on_delete=models.CASCADE)
class Task(models.Model):
name = models.CharField(max_length=50, default='New Model')
project = models.ForeignKey(Project, on_delete=models.CASCADE)
date_created = models.DateTimeField(default=timezone.now)
Here is my DeleteView Class:
class TaskDeleteView(LoginRequiredMixin, UserPassesTestMixin, DeleteView):
model = Task
template_name = 'tasks/confirm_delete.html' # template for deletion
success_url ='/projects/'
# Test user permission
def test_func(self):
task = self.get_object()
if self.request.user == task .project.author:
return True
else:
return False
def get_success_url(self):
project = self.object.project
return reverse_lazy('tasks-listview', kwargs={'pk': project.id })
and my URL patterns:
urlpatterns = [
path('tasks/<int:pk>/list/', TasksListview.as_view(), name='tasks-listview'),
path('tasks/<int:pk>/delete/', TaskDeleteView.as_view(), name='task-delete'),
]
and here is my delete temple:
<form method=" POST" enctype="multipart/form-data">
{% csrf_token %}
<fieldset class='form-group'>
<h4>Current Project: {{ object.project }}</h4>
<h4>Are you sure you want to delete task named {{ object.name }}? </h4>
</fieldset>
<div class="form-group">
<button class="btn btn-danger float-sm-right mr-1" type="submit">Yes, Delete</button>
<a class="btn btn-secondary float-sm-right mr-1" href="{% url 'task-detail' object.id %}">Cancel</a>
</div>
</form>
My delete view class contains:
def delete(self, request, *args, **kwargs):
self.get_object().delete()
messages.add_message(self.request, messages.SUCCESS, 'Entity removed with success.')
data = {'valid': True}
return JsonResponse(data)
Related
I recently started learning django and was making a CRM.
models.py:
class Complaint(models.Model):
SOURCE_CHOICES=(
('email','E-mail'),
('call','Call'),
('ticket','Ticket')
)
store_name=models.CharField(max_length=20)
store_contact_no=models.IntegerField(max_length=10)
store_id=models.CharField(max_length=7)
source=models.CharField(choices=SOURCE_CHOICES, max_length=10)
agent=models.ForeignKey("Agent", null = True, blank = True, on_delete=models.SET_NULL)
category=models.ForeignKey("Category", related_name="complaints", null = True, blank = True, on_delete=models.SET_NULL)
description = models.TextField()
customer = models.ForeignKey("Customer", null = True, blank = True, on_delete=models.SET_NULL)
def __str__(self):
return f"{self.store_name} {self.store_id}"
class Customer(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
def __str__(self):
return self.user.username
forms.py
class CustomerComplaintForm(forms.ModelForm):
class Meta:
model = Complaint
fields = (
'store_name',
'store_id',
'store_contact_no',
'description',
)
views.py
class
CustomerComplaintCreateView(OwnerCustomerAndLoginRequiredMixin,
generic.CreateView):
template_name = "customercomplaint_create.html"
form_class = CustomerComplaintForm
def get_success_url(self):
return "/complaints"
def form_valid(self, form):
complaint = form.save(commit=False)
complaint.Customer = self.request.user.username
complaint.source = 'ticket'
complaint.save()
return super(CustomerComplaintCreateView,
self).form_valid(form)
html template:
{% extends "base.html" %}
{% load tailwind_filters %}
{% block content %}
<div class="max-w-lg mx-auto">
<a class="hover:text-blue-500" href="/complaints">Go back to complaints </a>
<div class="py-5 border-t border-gray-200">
<h1 class="text-4xl text-gray-800"> Create a new complaint </h1>
</div>
<form method='post' class="mt-5">
{% csrf_token %}
{{ form|crispy }}
<button type="submit" class="w-full bg-blue-500 text-white hover:bg-blue-600 px-
3 py-2 rounded-md">Create</button>
</form>
</div>
{% endblock content %}
mixins.py
class OwnerCustomerAndLoginRequiredMixin(AccessMixin):
"""Verify that the current user is authenticated and is an owner or customer"""
def dispatch(self, request, *args, **kwargs):
if not request.user.is_authenticated or not request.user.is_owner and not request.user.is_customer:
return redirect("/complaints")
return super().dispatch(request, *args, **kwargs)
The problem here is that, the source field gets filled in the database with Ticket as intended. But the 'Customer' field is not populated with the username. 'Self.request.user.username' is not the problem here as the username is being printed correctly in the console.
The issue is complaint.Customer = self.request.user.username, you're trying to assign a username to a supposedly Customer object. Here's an approach you could take to solve the issue though.
Within the views.py file, the view class.
You could get the customer object and then assign it to the customer field on the complaint object.
from django.shortcuts import get_object_or_404
def form_valid(self, form):
complaint = form.save(commit=False)
customer = get_object_or_404(Customer, user=self.request.user) # recommended
# or
# customer = get_object_or_404(Customer, user__username__iexact=self.request.user.username)
if customer:
# Here on your model you have a lowercase `c` for the customer field, not 'C`
complaint.customer = customer # -> This will assign the customer object, "FK".
complaint.source = 'ticket'
complaint.save()
return super(CustomerComplaintCreateView, self).form_valid(form)
That should work.
this must be the User not the user name
Because Cutomer is User object not only the Uesrname
def form_valid(self, form):
complaint = form.save(commit=False)
complaint.Customer = self.request.user
complaint.source = 'ticket'
complaint.save()
im following a tutorial for the project but it does it on function views and im trying to do it on class based views
i get a ( The view blog.views.PostDetailView didn't return an HttpResponse object. It returned None instead.) error but thats not my concern now ... because the data(new comments) arent getting saved
so how can i save them with the post request and redirect to the same page of the DetailView
my urls
app_name = 'blog'
urlpatterns = [
path('', views.PostListView.as_view(), name='blog-home'),
path('blog/<slug:slug>/', views.PostDetailView.as_view() , name='post-detail'),
]
my models
class Post(models.Model):
options = (
('draft', 'Draft'),
('published', 'Published')
)
title = models.CharField(max_length=250)
slug = models.SlugField(max_length=250, unique_for_date='publish_date')
publish_date = models.DateTimeField(default=timezone.now)
author = models.ForeignKey(User, on_delete=models.CASCADE, related_name='blog_posts')
content = models.TextField()
status = models.CharField(max_length=10, choices=options, default='draft')
class Meta:
ordering = ('-publish_date',)
def __str__(self):
return self.title
def get_absolute_url(self):
return reverse('post-detail', kwargs={'slug': self.slug})
class Comment(models.Model):
post = models.ForeignKey(Post, on_delete=models.CASCADE, related_name='comments')
author = models.ForeignKey(User, on_delete=models.CASCADE)
content = models.TextField()
publish_date = models.DateTimeField(auto_now_add=True)
class Meta:
ordering = ('-publish_date',)
def __str__(self):
return f'Comment By {self.author}/{self.post}'
my forms
class AddCommentForm(forms.ModelForm):
content = forms.CharField(label ="", widget = forms.Textarea(
attrs ={
'class':'form-control',
'placeholder':'Comment here !',
'rows':4,
'cols':50
}))
class Meta:
model = Comment
fields =['content']
my views
class PostDetailView( DetailView):
model = Post
context_object_name = 'post'
template_name='blog/post_detail.html'
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
comments = Comment.objects.filter(post=self.object)
context['comments'] = comments
context['form'] = AddCommentForm()
return context
def post(self, request, *args, **kwargs):
pass
def form_valid(self, form):
form.instance.author = self.post.author
user_comment.post = self.post
user_comment.save()
return super().form_valid(form)
html
<form method="POST">
<div class="col-12">
<hr>
{% with comments.count as total_comments %}
<legend class="border-bottom mb-4">{{total_comments }} comment{{total_comments|pluralize }}</legend>
{% endwith %}
{% for c in comments%}
<div class ="col-md-12 mb-1rem" >
<p class="mb-0"><strong>{{c.author}}:</strong> {{c.content}}</p>
<small class="text-muted">{{ c.publish_date|date:'f A, Y'}}</small>
</div>
<br>
{% endfor %}
</div>
<hr>
{% csrf_token %}
<fieldset class="form-group">
<legend class="border-bottom mb-4">New Comment</legend>
{{ form|crispy }}
</fieldset>
<div class="form-group">
<button class="btn btn-dark btn-lg mt-1" type="submit">Publish</button>
</div>
</form>
The DetailView has no form_valid method.
It just shows the objects of the model.
Form processing is in the GenericEdit View class.
There are many ways, but...
In this code, you can create a GenericEdit View(CreateView or UpdateView ) url, process the form there(form_valid), and then
success_url = reverse_lazy('form:detail') # in GenericEdit View class
return the template.
To sum up,
add path in urls.py
like this... 👇
path('blog/<slug:slug>/update', views.PostUpdateView.as_view(), name='post-update'),
add update or create url in form action.
ex. ☞ action="{% url 'blog:post-update' %}"
make GenericEdit View class☞ views.PostUpdateView
ps.You can also use forms.py
"""
When you use Class Base View in django, it is recommended to refer to this site.
https://ccbv.co.uk/
and,
DetailView refer is here
https://ccbv.co.uk/projects/Django/3.0/django.views.generic.detail/DetailView/
"""
I know it's been a long time, but I think someone might need the answer in future.
I'm using django 3.2.16.
in your post method inside DetailView:
Update your post method to:
def post(self, request, *args, **kwargs):
# Get the current pk from the method dictionary
pk = kwargs.get('pk')
if request.method == 'POST':
# Get the current object
obj = self.model.objects.get(id=pk)
# Alter the field Value
some_value = request.POST.get('some_value_from_html_input')
obj.field = some_value
# Save the object
obj.save()
# Redirect to you current View after update
return redirect(current_details_view, pk=pk)
I'm having an issue when submitting a form within a class-based view. I'm using the FormMixin in a detail view, and when I submit the form I get a 405 error. I've tried chopping and changing the code in views, but nothing seems to be working.
Models
class Bid(models.Model):
author = models.ForeignKey(User, on_delete=models.CASCADE)
tender = models.ForeignKey(Tender, on_delete=models.CASCADE, null=True)
title = models.CharField(max_length=100)
specification = models.TextField()
timeline = models.CharField(max_length=100)
date_posted = models.DateTimeField(default=timezone.now)
price = models.IntegerField()
def __str__(self):
return self.title
def get_absolute_url(self):
return reverse('bid-detail', kwargs={'pk': self.pk})
Views
class TenderDetailView(FormMixin, LoginRequiredMixin, DetailView):
model = Tender
template_name = 'it_me/tender_detail.html'
form_class = BidForm
def get_success_url(self):
return reverse('tender-detail', kwargs={'pk': self.object.id})
def get_context_data(self, **kwargs):
context = super(TenderDetailView, self).get_context_data(**kwargs)
context['form'] = BidForm(initial={'post': self.object})
return context
def post(self, request, *args, **kwargs):
self.object = self.get_object()
form = self.get_form()
if form.is_valid():
return self.form_valid(form)
else:
return self.form_invalid(form)
def form_valid(self, form):
form.save()
return super(TenderDetailView, self).form_valid(form)
forms
class BidForm(forms.ModelForm):
class Meta:
model = Bid
fields = ('title', 'specification', 'timeline', 'price')
Templates
<div class="content-section">
<form method="POST">
{% csrf_token %}
<fieldset class="form-group">
{{ form|crispy }}
</fieldset>
<div class="form-group">
<button class="btn btn-outline-info" type="submit"> Post Bid </button>
</div>
</form>
</div>
{% for bid in tender.bids.all %}
<div class=" col-md-12 comment">
<div class="date">{{ bid.date_posted|date:"F d, Y" }}</div>
<strong>{{ bid.author }}</strong>
</div>
{% empty %}
<p>No Bids Yet </p>
{% endfor %}
urls
path('tender/<int:pk>/', TenderDetailView.as_view(), name='tender-detail')
Would really appreciate any help cause I am stumped.
DetailView has no support for post requests. It is aimed to show instance details. As you can see in the docs, the view supports only get method. To update your instance, you can use UpdateView. UpdateView is the generic view to handle update operation. You can also use these documentation pages for basic practices such as showing the instance details in DetailView.
I have a CreateView class and I'm trying to use the input from a multiple choice field it has as part of the success_url.
It works on my TopicCreateView class because the input is a charfield, but when I try to get it to work on PostCreateView it returns a KeyError. From what i understand it's because it returns the value of the multiple choice field(1, 2, 3 etc) instead of the text between the option tags.
The Topic part works fine.
views.py
class TopicCreateView(LoginRequiredMixin, CreateView):
model = Topic
template_name = 'topic_form.html'
fields = ['board', 'title']
success_url = '/topic/{title}'
def form_valid(self, form):
form.instance.author = self.request.user
return super().form_valid(form)
And here is the models.py
class Topic(models.Model):
title = models.CharField(max_length=100, unique=True)
board = models.ForeignKey(Board, default='ETC', on_delete=models.SET_DEFAULT)
date_published = models.DateTimeField(default=timezone.now)
def __str__(self):
return self.title
Here is what I can't get to work, the Post.
views.py
class PostCreateView(LoginRequiredMixin, CreateView):
model = Post
template_name = 'post_form.html'
fields = ['topic', 'content']
success_url = '/topic/{topic}'
def form_valid(self, form):
form.instance.author = self.request.user
return super().form_valid(form)
and models.py
class Post(models.Model):
content = models.TextField()
author = models.CharField(max_length=200, default='Unknown', blank=True, null=True)
date_published = models.DateTimeField(default=timezone.now)
topic = models.ForeignKey(Topic, default=content, on_delete=models.SET_DEFAULT)
def __str__(self):
return self.topic
Also, the form is the same for both of them:
{% extends '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">Create A New Post</legend>
{{ form | crispy }}
</fieldset>
<div class="form-group">
<button class="btn btn-outline-info" type="submit">Submit</button>
</div>
</form>
</div>
{% endblock %}
So, when I redirect to the newly created topic/thread it works, but I can't do the same for new posts.
I'm new to Django! so could be way wrong :)
I'm building an event app.
I'm trying to add/remove users to a 'host' list in my 'EventProfile' model from 'MyGuest', i basically want a different host list in each 'EventProfile', i'm using a class method.
I can add/delete each object to each specific EventProfile model in the admin but cant seem to do it on my site?
I keep getting this error "get() returned more than one Host -- it returned 2!"
Heres my code please help :)
Models.py
class MyGuest(models.Model):
users = models.ManyToManyField(User)
current_user = models.ForeignKey(User, related_name='add_myguest', null=True)
#classmethod
def make_myguest(cls, current_user, new_myguest):
myguest, created = cls.objects.get_or_create(current_user=current_user)
myguest.users.add(new_myguest)
#classmethod
def lose_myguest(cls, current_user, new_myguest):
myguest, created = cls.objects.get_or_create(current_user=current_user)
myguest.users.remove(new_myguest)
class Host(models.Model):
users = models.ManyToManyField(User)
current_user = models.ForeignKey(User, related_name='add_myguest', null=True)
#classmethod
def make_host(cls, current_user, new_host):
host, created = cls.objects.get_or_create(current_user=current_user)
host.users.add(new_host)
#classmethod
def lose_host(cls, current_user, new_host):
host, created = cls.objects.get_or_create(current_user=current_user)
host.users.remove(new_host)
class EventProfile(models.Model):
event = models.CharField(max_length=100)
greeting = models.CharField(max_length=100)
invitee = models.CharField(max_length=100)
description = models.CharField(max_length=100)
date = models.CharField(max_length=100)
start_time = models.CharField(max_length=100)
finish_time = models.CharField(max_length=100)
venue = models.CharField(max_length=100)
myguest = models.ForeignKey(MyGuest, related_name='add_myguest_profile', null=True)
host = models.ForeignKey(Host, related_name='add_host_profile')
def __str__(self):
return self.event
Views.py
def event_change_hosts(request, operation, pk):
new_host = User.objects.get(pk=pk)
if operation == 'add':
Host.make_host(request.user, new_host)
return redirect('events:event_myguests_profile')
elif operation == 'remove':
Host.lose_host(request.user, new_host)
return redirect('events:event_hosts')
urls.py
url(r'^event_hosts/(?P<pk>\d+)$', views.event_hosts, name= 'event_hosts'),
url(r'^connect_hosts/(?P<operation>.+)/(?P<pk>\d+)/$', views.event_change_hosts, name= 'event_change_hosts'),
template.py
{% for host in hosts %}
<div class="alert alert-info" role="alert">
<a href="{% url 'accounts:profile_with_pk' pk=user.pk %}" type="button"
class="btn btn-primary left"
class="btn btn-default btn-lg active">{{ host.username }}
<a href="{% url 'events:event_change_hosts' operation='remove' pk=user.pk %}">
<button type="button" class="btn btn-danger pull-right btn-s">Remove</button>
</a>
</div>
{% endfor %}