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>
Related
In my template I have a form that includes two input elements whose values can be adjusted with javascript. I want to be able to take these values and, on form submit, display them in a sentence in a for loop underneath.
index.html:
<form action="{% url 'workouts:workout' %}" method="post">
{% csrf_token %}
<div class="weight">
<h4>WEIGHT (kgs):</h4>
<button type="button" class="weight-dec">-</button>
<input type="text" value="0" class="weight-qty-box" readonly="" name="one">
<button type="button" class="weight-inc">+</button>
</div>
<div class="reps">
<h4>REPS:</h4>
<button type="button" class="rep-dec">-</button>
<input type="text" value="0" class="rep-qty-box" readonly="" name="two">
<button type="button" class="rep-inc">+</button>
</div>
<input type="submit" value="Save" name="submit_workout">
<input type="reset" value="Clear">
</form>
{% if exercise.workout_set.all %}
{% for w in exercise.workout_set.all %}
{{ w.content }}
{% endfor %}
{% endif %}
I have given the form above an action attribute for a url which maps to a view, and each of the inputs has a name in order to access their values in the view. I also have written this form in forms.py:
class WorkoutModelForm(forms.ModelForm):
class Meta:
model = Workout
fields = ['content']
And for context, here is my model:
class Workout(models.Model):
content = models.CharField(max_length=50)
created = models.DateField(auto_now_add=True)
updated = models.DateField(auto_now=True)
exercise = models.ForeignKey(Exercise, on_delete=models.CASCADE, default=None)
class Meta:
ordering = ('created',)
My problem from here is that I have no idea how to actually incorporate my model form in my template, or how to write a view that will do what I want it to. I am still new to this and have been searching for an answer for sometime, but so far have not found one. Please help.
This is able to help you, you should first have a look at the django Class-Based Views , more specifically the FormView, django already has generic views capable of handling data posted on forms. Your code would look like this:
# forms.py
# imports ...
class WorkoutModelForm(forms.ModelForm):
class Meta:
model = Workout
fields = ['content']
# urls.py
from django.urls import path
from . import views
app_name = 'myapp'
urlpatterns = [
path("test-form/", views.TesteFormView.as_view(), name='test-form'),
]
# views.py
from django.views.generic import FormView
from myapp import forms
from django.contrib import messages
class TesteFormView(FormView):
template_name = "myapp/index.html"
success_url = reverse_lazy('myapp:test-form')
form_class = forms.WorkoutModelForm
def get(self, request, *args, **kwargs):
return super(TesteFormView, self).get(request, *args, **kwargs)
def form_valid(self, form):
print(f"POST DATA = {self.request.POST}") # debug
content = form.cleaned_data.get('content')
# fieldx= form.cleaned_data.get('fieldx')
# do something whit this fields like :
Workout.object.create(content=content)
messages.success(self.request,"New workout object created")
return super(TesteFormView, self).form_valid(form=self.get_form())
def form_invalid(self, form):
print(f"POST DATA = {self.request.POST}") # debug
for key in form.errors:
messages.error(self.request, form.errors[key])
return super(TesteFormView, self).form_invalid(form=self.get_form())
And your template would look like:
# myapp/index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>TestForm</title>
</head>
<body>
<form method="post">
{% csrf_token %}
{{ form }}
<button type="submit">submit</button>
</form>
</body>
</html>
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')
I am very new in Django. Not sure whether this is a bug or an error.
Here is my model in an app called gcbv (for generic class-based view)
from django.db import models
from core.models import TimeStampModel
from django.urls import reverse
# Create your models here.
class Vehicle(TimeStampModel):
maker = models.CharField(max_length=100)
model_year = models.IntegerField()
vehicle_type = models.CharField(max_length=100)
slug = models.SlugField(max_length=100, unique=True)
vehicle_model = models.CharField(max_length=100)
website = models.URLField(max_length=100, blank=True)
email = models.EmailField(max_length=100, blank=True)
notes = models.TextField(blank=True, default='')
def __str__(self):
x = self.maker + ' ' + self.vehicle_model
return x
And here are the URLs:
from django.contrib import admin
from django.urls import path, include
from django.conf.urls import url
from . import views
from django.urls import reverse
#from django.views.generic.base import TemplateView
app_name = 'gcbv'
urlpatterns = [
path('sub1/', views.SubView.as_view(), name='sub1'),
path('vehicle_list/', views.VehicleListView.as_view(),
name = 'vehicle_list'),
path('vehicle/<str:slug>/',
views.VehicleDetailView.as_view(),
name='vehicle_detail'),
path('vehicle/create', views.VehicleCreateView.as_view(),
name='vehicle_create'),
path('', views.IndexTemplateView.as_view(), name='index'),
]
And here is the relevant view:
class VehicleCreateView(CreateView):
model = Vehicle
fields = ['maker', 'model_year', 'vehicle_type', 'slug',
'vehicle_model', 'website', 'email', 'notes']
labels = {'maker':'Maker', 'model_year':'Year',
'vehicle_type':'Type', 'vehicle_model':'Model',
'website':'Website', 'email':'Email', 'notes':'Notes'}
Here is the template:
{% extends "core/base.html" %}
{% block body_block %}
<h1>Vehicle Create for GCBV</h1>
<form action="POST" action="">
{% csrf_token %}
{{ form.as_p }}
<button name="submit" class="btn btn-primary">Submit</button>
</form>
<h1>End Vehicle Create for GCBV</h1>
{% endblock %}
It looks as if the data aren't saved in the database, but when i'm adding the same data by hand directly in the admin page, everything works fine. I've attached another screenshot showing that VehicleDetailView has found the relevant template and rendered the information.
Any help would be greatly appreciated.
NB: Everything worked fine when I use function views and regex instead of path.
Form
After submit
List
Details
OK, this is what we septuagenarians call a "senior moment". I have been staring at this code for two days and did not see the obvious.
method="POST"!
NOT
action="POST"
Many, many thanks
In the fourth line of your template, method should be equal to "post"
{% extends "core/base.html" %}
{% block body_block %}
<h1>Vehicle Create for GCBV</h1>
<form method="POST" action="">
{% csrf_token %}
{{ form.as_p }}
<button name="submit" class="btn btn-primary">Submit</button>
</form>
<h1>End Vehicle Create for GCBV</h1>
{% endblock %}
I've been trying to determine where this validation/error message and styling is coming from but I can't narrow it down? I've reduced the template to a basic template so that it isn't loading base.html any longer but the tooltip still appears somehow.
However the tooltip and message don't appear in safari - it just defaults to the standard django email validation message "Enter a valid email address".
Note I have selected don't use disable cache in chrome dev tools (network) but that didn't help.
Form:
class EmailTestForm(forms.Form):
email = forms.EmailField()
View:
class EmailTestFormView(FormView):
form_class = EmailTestForm
template_name = "site/test_email.html"
success_url = "/signup"
def form_valid(self, form):
print('form is good')
Template:
{% block inner %}
<form action="" method="post" >{% csrf_token %}
{% crispy form %}
<input id="submit" class="btn btn-block btn-cta-primary" type="submit"/>
</form>
{% endblock inner %}
urls.py
from django.conf.urls import url
from . import views
urlpatterns = [
url(r'^signup/$', views.signup_view, name="signup"),
url(r'^emailtest/$', views.EmailTestFormView.as_view(), name="email_test"),
]
I'm just doing a quick test of a CBV Formview. However for some reason the form won't submit and I've gone blind from looking at it to find out why. There is no error shown, when I click on submit nothing happens and it doesn't redirect to the success url or print out the test message.
Form:
class EmailTestForm(forms.Form):
email = forms.EmailField()
View:
class EmailTestFormView(FormView):
form_class = EmailTestForm
template_name = "site/test_email.html"
success_url = "/signup"
def form_valid(self, form):
print('form is good')
Template:
{% extends "site/signup.html" %}
{% load crispy_forms_tags %}
{% block inner %}
<form action="" method="post" >{% csrf_token %}
{% crispy form %}
<input id="submit" class="btn btn-block btn-cta-primary" type="submit"/>
</form>
{% endblock inner %}
urls.py
from django.conf.urls import url
from . import views
urlpatterns = [
url(r'^signup/$', views.signup_view, name="signup"),
url(r'^emailtest/$', views.EmailTestFormView.as_view(), name="email_test"),
]
this is caused by using cripsy forms to render the form, which automatically inserts a <form> tag when rendering the form.
From cripsy form docs:
form_tag = True
It specifies if <form></form> tags should be rendered when using a Layout. If set to False it renders the form without the <form></form> tags. Defaults to True.