Why this DeleteView doesn't actually delete the entry in Django? - python

In my Django app the delete view doesn't work. When I click on an entry the page just refreshes and nothing happens. The entry is not deleted from the database. Creating and updating entries works fine. Is there something wrong with any of the files?
microtarjetas/views.py:
class DeleteCardView(DeleteView):
model = Card
template_name = 'microtarjetas/delete_card.html'
form_class = CardForm
success_url = '/cards/'
templates/microtarjetas/delete_card.html:
{% extends 'base.html' %} {% load static %} {% block content %}
<h2>Delete card</h2>
<p>Are you sure you want to delete "{{ object }}"?</p>
<form method="post">{% csrf_token %}
<input type="submit" value="Confirm" />
</form>
{% endblock %}
a snippet from templates/microtarjetas/cards.html:
<tbody>
{% for card in cards %}
<tr>
<td>{{ card.word_en }}</td>
<td>{{ card.word_es }}</td>
<td>update</td>
<td>X</td>
</tr>
{% endfor %}
</tbody>
forms.py:
from django import forms
from .models import Card
class CardForm(forms.ModelForm):
class Meta:
model = Card
fields = "__all__"
models.py:
class Card(models.Model):
word_en = models.CharField(max_length = 30)
word_es = models.CharField(max_length = 30)
def get_absolute_url(self):
return "/cards"
def __str__(self):
return f"{self.word_en} --- {self.word_es}"
urls.py:
from django.urls import path
from . import views
urlpatterns = [
path('', views.HomeView.as_view(), name='home'),
path('cards/', views.CardsView.as_view(), name='cards'),
path('cards/new', views.CreateCardView.as_view(), name='create_card'),
path('cards/<pk>/delete/', views.DeleteCardView.as_view(), name='delete_card'),
path('cards/<pk>/update/', views.UpdateCardView.as_view(), name='update_card'),
]
I add a {{ form }} tag to the delete_card.html form, it works (although it adds two input fields to the form which are unnecessary):
delete_card.html

You are not submitting the form with the form data. You should update the code like this:
<form method="post">{% csrf_token %}
{{ form }}
<input type="submit" value="Confirm" />
</form>
More information can be found in the documentation.

Related

Invalid block tag on line 24: 'form.as_p', expected 'endblock'. Did you forget to register or load this tag?

I'm a new in Django and trying to do a app but now I'm having this error: "Invalid block tag on line 24: 'form.as_p', expected 'endblock'."
TEMPLATE
{% extends "base.html" %}
{% block title %}
<title>Tarefas</title>
{% endblock title %}
{% block content %}
<div class="content">
{% if messages %}
<div class="container">
<br>
{% for message in messages %}
<div class="alert alert-info" role="alert">
{{message}}
</div>
{% endfor %}
</div>
{% endif %}
<div class="container tasks-box">
<table class="table table-striped">
<form method='POST'>
{% csrf_token %}
{% form.as_p %}
<button type="submit" class="btn btn-secondary btn-sec">Adicionar</button>
</form>
[...]
forms.py
from django.forms import ModelForm
from todolist.models import Tasks
class TaskForm(ModelForm):
class Meta:
model = Tasks
fields = ['task','responsible']
views.py
from django.shortcuts import render, redirect
from django.http import HttpResponse
from todolist.models import Tasks
from todolist.forms import TaskForm
from django.contrib import messages
from django.contrib.auth.decorators import login_required
#login_required
def todolist(request):
if request.method == 'POST':
form = TaskForm(request.POST or None)
if form.is_valid():
instance = form.save(commit=False)
instance.manager = request.user
instance.save()
messages.success(request,("Tarefa adicionada"))
return redirect('todolist')
else:
form = TaskForm
all_tasks = Tasks.objects.filter(manager=request.user)
all_users = Tasks.objects.all()
return render(request,'todolist.html',{ 'form':form,
'all_tasks':all_tasks,
'all_users':all_users})
models.py
from django.db import models
from django.contrib.auth.models import User
from django.contrib.auth import get_user_model
# Create your models here.
User = get_user_model()
class Tasks(models.Model):
manager = models.ForeignKey(User, on_delete=models.CASCADE,default=None, related_name='tasks_manager')
task = models.CharField(max_length=300)
done = models.BooleanField(default=False)
responsible = models.ForeignKey(User, on_delete=models.CASCADE, default=None, related_name='tasks_responsible', blank=True, null=True)
def __str__(self):
return self.task
I tryed don't use {{ form }} tag in template and thats work.
I think the problem is in views, but i can't figure out why.
Someone can help me?
From form rendering options as documented
All you need to do to get your form into a template is to place the form instance into the template context. So if your form is called form in the context, {{ form }} will render its and elements appropriately.
There are other output options though for the / pairs:
{{ form.as_table }} will render them as table cells wrapped in <tr> tags
{{ form.as_p }} will render them wrapped in <p> tags
{{ form.as_ul }} will render them wrapped in <li> tags
So you have a typo error in {% form.as_p %} just replace this with {{ form.as_p }}

Multiple file upload the easy way in django

I have to create a form to make a post with fields like Title, description and images. I could successfully create a post with a single image, but when it comes to multiple images, it is not getting saved. Upon research found that i have to use a separate model and form for images. But i cannot see how is it accomplished in the views. So how do i accomplish this the easy way?
Models.py
class news(models.Model):
title = models.CharField(max_length=100)
description= models.TextField(max_length=500)
class newsimages(models.Model):
image = models.ImageField(upload_to='/images/')
related = models.ForeignKey(news,on_delete=models.CASCADE,null=True)
You can do with inline_formset
create a form
class ImageForm(ModelForm):
class Meta:
model = newsimages
exclude = ()
ImageFormSet = inlineformset_factory(news, newsimage, form=ImageForm, extra=1)
You CreateView would be something like:
class NewsCreate(CreateView):
model = news
fields = ['title', 'description']
success_url = some_url
def get_context_data(self, **kwargs):
ctx = super().get_context_data(**kwargs)
ctx['images'] = ImageFormSet(self.request.POST or None)
return ctx
def form_valid(self, form):
ctx = self.get_context_data()
images = context['images']
with transaction.atomic():
self.object = form.save()
if images .is_valid():
images.instance = self.object
images.save()
return super().form_valid(form)
in news_form.html
<form method="post" enctype="multipart/form-data">
{{ form.as_p }}
<table>
{{images.management_form}}
{% for f in images.forms %}
{% if forloop.first %}
<thead>
<tr>
{% for field in form.visible_fields %}
<th>{{ field.label|capfirst }}</th>
{% endfor %}
</tr>
</thead>
{% endif %}
<tr class="{% cycle row1,row2 %} formset_row">
{% for field in form.visible_fields %}
<td>
{# Include the hidden fields in the form #}
{% if forloop.first %}
{% for hidden in form.hidden_fields %}
{{ hidden }}
{% endfor %}
{% endif %}
{{ field.errors.as_ul }}
{{ field }}
</td>
{% endfor %}
</tr>
{% endfor %}
</table>
<input type="submit" value="Save"/>
</form>
</div>
<script src="//ajax.googleapis.com/ajax/libs/jquery/2.1.3/jquery.min.js"></script>
<script src="{% static 'formset/jquery.formset.js' %}"></script>
<script type="text/javascript">
$('.formset_row').formset({
addText: 'add image',
deleteText: 'remove',
prefix: 'images_set'
});
</script>
You will need to source jquery.formset.js which I think you'll find in django admin static files.

How to send data from CBV form to a template CBV?

I'm trying to send the POST data from a CBV form to other CBV.
I use the valid_form method to get the info by form.cleaned_data and then i apply in this method some custom methods.
But I cant send the result to the other view.
Also I tried put an action in the html sending to the other template and then grab the data but I cant.
views.py
from django.shortcuts import render
from django.views.generic.edit import FormView
from django.views.generic.base import TemplateView
from .forms import QuoteForm
from .models import SmgQuotesTable
from .quotes import Quotes
class QuoteFormView(FormView):
template_name = 'core/home.html'
form_class = QuoteForm
success_url = 'quotes'
def form_valid(self, form):
name = form.cleaned_data['name']
email = form.cleaned_data['email']
couple = form.cleaned_data['couple']
age = form.cleaned_data['age']
kids = form.cleaned_data['kids']
#query_filter = Quotes().planSelector(couple, kids, age)
#obj = SmgQuotesTable.objects.filter(composite='Ind. Junior (H25)')
return super(QuoteFormView, self).form_valid(form)
class QuoteListView(ListView):
model = SmgQuotesTable
def get_queryset(self):
queryset = super(QuoteListView, self).get_queryset()
queryset = queryset #
print(queryset)
return queryset
home.html
{% block content %}
<style>label{display:none}</style>
<form method="post" action="{% url 'quotes-list' %}">{% csrf_token %}
{{ form.as_p }}
<input type="submit" class="btn btn-primary btn-block py-2" value="Cotizar">
</form>
{% endblock %}
urls.py
from django.urls import path
from .views import QuoteFormView, QuoteListView
urlpatterns = [
path('', QuoteFormView.as_view(), name='home'),
path('quotes/', QuoteListView.as_view(), name='quotes-list'),
]
I expect to grab the name, email, couple, age and kids values in the QuoteView and apply the Quotes method so i can print the result in the quotes.html
I solve it.
In views.py replace the form_Valid method in the form view with the get_query method in the ListView.
With the self.request.GET() I get the info and pase it to the custom quot method.
Then i return the filtered information with the result.
from django.shortcuts import render
from django.views.generic.edit import FormView
from django.views.generic.list import ListView
from .forms import QuoteForm
from .models import SmgQuotesTable
from .quotes import Quotes
class QuoteFormView(FormView):
template_name = 'core/home.html'
form_class = QuoteForm
success_url = 'quotes'
class QuoteListView(ListView):
model = SmgQuotesTable
def get_queryset(self):
r_get = self.request.GET
d_get = {'name': None , 'email':None , 'couple': None, 'age': None, 'kids':None ,}
for value in d_get:
d_get[value] = r_get[value]
query_filter = Quotes().planSelector(d_get['couple'], d_get['kids'], d_get['age'])
queryset = super(QuoteListView, self).get_queryset().filter(composite=query_filter)
return queryset
In home.html have to change the POST method to a GET method, because the list view dont allow POST.
Adding in the the action to the ListView template I send the data in the form to takeit with the self.request.GET in the get_queryset() method.
{% extends 'core/base.html' %}
{% block title %}Cotizador{% endblock %}
{% load static %}
{% block headers %}
<h1>Cotizador</h1>
<span class="subheading">Cotiza tu plan!</span>
{% endblock %}
{% block content %}
<style>label{display:none}</style>
<form method="get" action="{% url 'quotes-list' %}">{% csrf_token %}
{{ form.as_p }}
<input type="submit" class="btn btn-primary btn-block py-2" value="Cotizar">
</form>
{% endblock %}
Finaly i put the information in the smgquotestable_list using the object_list and the properties of the model.
{% extends 'core/base.html' %}
{% block title %}Cotización{% endblock %}
{% load static %}
{% block headers %}
<h1>Cotización</h1>
<span class="subheading">Cotización</span>
{% endblock %}
{% block content %}
{% for item in object_list %}
<div class="table-responsive text-nowrap"></div>
<table class="table table-striped">
<thead>
<tr>
<th scope="col"></th>
<th scope="col">SMG01</th>
<th scope="col">SMG02</th>
<th scope="col">SMG10</th>
<th scope="col">SMG20</th>
<th scope="col">SMG30</th>
<th scope="col">SMG40</th>
<th scope="col">SMG50</th>
<th scope="col">SMG60</th>
<th scope="col">SMG70</th>
</tr>
</thead>
<tbody>
<tr>
<th scope="row">{{ item.composite }}</th>
<td>$ {{ item.smg01 }}</td>
<td>$ {{ item.smg02 }}</td>
<td>$ {{ item.smg10 }}</td>
<td>$ {{ item.smg20 }}</td>
<td>$ {{ item.smg30 }}</td>
<td>$ {{ item.smg40 }}</td>
<td>$ {{ item.smg50 }}</td>
<td>$ {{ item.smg60 }}</td>
<td>$ {{ item.smg70 }}</td>
</tr>
</tbody>
</table>
</div>
{% endfor %}
{% endblock %}

Django wrong request.POST attribute value saved

I am experiencing the issue that the code saves request.POST['name'] value to assumptions.Name field in database, but only the last value in the list which is 'B' in this case. How could I make it save both prefixes 'A' and 'B' to database field Name based on respective forms. When I debug I see both 'A' and 'B' with key 'name'.
views.py
from django.shortcuts import render
from .forms import modelformset_factory, AssumptionsForm
from .models import Assumptions
from django.core.exceptions import ValidationError
model_names = ['A', 'B']
def get_assumptions(request):
AssumptionFormset = modelformset_factory(
Assumptions, form=AssumptionsForm, extra=5)
if request.method == 'POST':
formsets = [AssumptionFormset(request.POST, prefix=thing) for thing in model_names]
if all([formset.is_valid() for formset in formsets]):
for formset in formsets:
for form in formset:
assumption_data = form.save(commit=False)
assumption_data.Name = request.POST.get('name')
assumption_data.save()
else:
formsets = [AssumptionFormset(prefix=thing) for thing in model_names]
return render(request, 'assumptions.html', {'formsets': formsets})
assumptions.html
<div class="form">
<form action="" method="post">
{% for formset in formsets %}
{% csrf_token %}
{{ formset.management_form }}
{{ formset.non_form_errors.as_ul }}
<h1>{{formset.prefix}}</h1>
<table id="formset" class="form">
{% for form in formset.forms %}
{% if forloop.first %}
<thead><tr>
{% for field in form.visible_fields %}
<th>{{ field.label|capfirst }}</th>
{% endfor %}
</tr></thead>
{% endif %}
<tr class="{% cycle 'row1' 'row2' %}">
{% for field in form.visible_fields %}
<td>
{# Include the hidden fields in the form #}
{% if forloop.first %}
{% for hidden in form.hidden_fields %}
{{ hidden }}
{% endfor %}
{% endif %}
{{ field.errors.as_ul }}
{{ field }}
</td>
{% endfor %}
</tr>
{% endfor %}
</table>
<input type="hidden" id={{formset.prefix}} name='name', value={{formset.prefix}} />
{% endfor %}
<input type="submit" value="Submit">
</form>
</div>
models.py
from django.db import models
from django.forms import ModelForm
class Assumptions(models.Model):
Worst = models.FloatField(null=True, blank=True, default=None)
Base = models.FloatField(null=True, blank=True, default=None)
Best = models.FloatField(null=True, blank=True, default=None)
Name = models.TextField(null=True, blank=True, default=None)
forms.py
from django import forms
from django.forms import modelformset_factory, ModelForm
from .models import Assumptions
class AssumptionsForm(ModelForm):
class Meta:
model = Assumptions
fields = '__all__'
exclude = ['Name']
I figured it out. I should have used getlist method and then loop through values.
values = request.POST.getlist('name')
Cheers.

Django modelformset_factory UnboundLocalError

I am getting error message the UnboundLocalError local variable 'Assumptions' referenced before assignment (after line else:). I wonder how is that even possible given that Assumptions is a model and is imported to the views.py. Any advise how to solve it would be highly appreciated. Also, I am trying to create multiple forms based on Assumptions modelform and save them into the database model Assumptions. Please advise if this code is the correct design pattern. Thank you.
views.py
from django.shortcuts import render
from .forms import modelformset_factory, AssumptionsForm
from .models import Assumptions
def get_assumptions(request):
if request.method == 'POST':
if 'name' in request.POST:
formset = modelformset_factory(Assumptions, form = AssumptionsForm, extra = 5)
if formset.is_valid():
print('valid form')
for form in formset:
print('Looping forms')
Assumptions = form.save(commit='False')
Assumptions.Name = 'name'
Assumptions.save()
else:
formset = modelformset_factory(Assumptions, form = AssumptionsForm, extra = 5)
return render(request, 'assumptions.html', {'formset': formset})
assumptions.html
<div class="form">
<form action="" method="post">
{% csrf_token %}
{{ formset.management_form }}
{{ formset.non_form_errors.as_ul }}
{% for name in ['A', 'B'] %}
<h1>var</h1>
<table id="formset" class="form">
{% for form in formset.forms %}
{% if forloop.first %}
<thead><tr>
{% for field in form.visible_fields %}
<th>{{ field.label|capfirst }}</th>
{% endfor %}
</tr></thead>
{% endif %}
<tr class="{% cycle 'row1' 'row2' %}">
{% for field in form.visible_fields %}
<td>
{# Include the hidden fields in the form #}
{% if forloop.first %}
{% for hidden in form.hidden_fields %}
{{ hidden }}
{% endfor %}
{% endif %}
{{ field.errors.as_ul }}
{{ field }}
</td>
{% endfor %}
</tr>
{% endfor %}
</table>
<input type="submit", name='name', value="save" />
{% endfor %}
</form>
</div>
forms.py
from django import forms
from django.forms import modelformset_factory, ModelForm
from .models import Assumptions
class AssumptionsForm(ModelForm):
class Meta:
model = Assumptions
fields = ['Worst', 'Base', 'Best']
exclude = ['Name']
models.py
from django.db import models
from django.forms import ModelForm
class Assumptions(models.Model):
Worst = models.FloatField(null=True, blank=True, default=None)
Base = models.FloatField(null=True, blank=True, default=None)
Best = models.FloatField(null=True, blank=True, default=None)
Name = models.TextField(null=True, blank=True, default=None)
You are shadowing the imported Assumptions name by assigning a new value to it here:
Assumptions = form.save(commit='False')
You should use a different name for your model instance. The usual practice is to use a lowercase name, i.e.
assumptions = form.save(commit='False')
assumptions.Name = 'name'
assumptions.save()

Categories