Django: Updating and Deleting objects using Django Views - django

The task which I am trying to achieve is to give the power to the logged-in user to update and delete their contact list via Django views. I coded all the templates and views.py functionality but has not been able to proceed forward.
Here is what I was able to achieve:
"views.py":
#login_required
def update_contact(request,contact_id=1):
current_user = request.user.get_username()
user = User.objects.filter(username=current_user).first()
output = UserContacts.objects.filter(current_user_id=user.id)
count = output.count()
contact_obj =get_object_or_404(UserContacts,id = contact_id)
form = UserContactForm()
if request.method == "POST":
form = UserContactForm(request.POST,instance=contact_obj)
if form.is_valid():
form.save(commit=True)
return index(request)
else:
print('Error Form Invalid')
my_dict = {'output':output,'form':form}
return render(request,'basic_app/update.html',my_dict)
Here is my template for update.html for updating contact:
{%extends 'basic_app/base.html' %}
{% block body_block %}
<div class="jumbotron">
<h1>Welcome to update contact page</h1>
<br />
{% if output %}
<table class="table table-striped">
<thead class="thead-dark">
<th>First Name</th>
<th>Edit</th>
</thead>
{% for contact in output %}
<tr>
<td>{{ contact.first_name}}</td>
<td>Edit</td>
</tr>
{% endfor %}
</table>
{% else %}
<div class="alert alert-warning" role="alert">
No contact records found!
</div>
{% endif %}
</div>
{% endblock %}
URLS.Py:
app_name = 'basic_app'
urlpatterns = [
url(r'^register/$',views.register,name='register'),
url(r'^user_login/$',views.user_login,name='user_login'),
url(r'^new_contact/$',views.new_contact,name='new_contact'),
url(r'^view_contacts/$',views.view_contacts,name='view_contacts'),
url(r'^update/(?P<contact_id>[\d]+)$',views.update_contact,name='update'),
url(r'^delete/$',views.delete_contact,name="delete")
]
Here is the error I am currently stuck with
Any help would be greatly appreciated!

In your urls.py the name of the update url is update:
url(r'^update/(?P<contact_id>[\d]+)$',views.update_contact,name='update')
But in your template your are using update_contact as name.
<td>Edit</td>
Try first to change the name in the template:
<td>Edit</td>

Related

Django object update using htmx and SingleObjectMixin

I'm using htmx for the first time. I have a table where each cell is a grade object. Previous to htmx I made each cell a link to an UpdateView for the object. I am now trying to have the user modify the the object's score field directly in the table using htmx. I'm sure I'm doing several things wrong.
My page loads as expected and the table is displayed as expected. when I type in a cell, I get an error Forbidden 403. CSRF Verification Failed.
The purpose of this post/question is to figure out how to get past this 403 error. Having said that, if it turns out that I'm going down the completely wrong path with using a SingleObjectMixin, please let me know.
View
class GradeChange(SingleObjectMixin, View):
""" view to handle htmx grade change"""
model = Grade
def post(self, request, *args, **kwargs):
grade = self.get_object()
assessment = Assessment.objects.get(grade=grade.pk)
print(assessment)
classroom = Classroom.objects.get(classroom=grade.cblock)
print(classroom)
if grade.score == "EXT" or grade.score=="APP" or grade.score=="DEV" or grade.score=="BEG":
grade.save()
return HttpResponse("S")
else:
return HttpResponse("")
template
<table class="table table-bordered table-sm">
<thead>
<tr>
<th class="col-3" scope="col">Students</th>
{% for obj in objective_list %}
<th class="col-2" scope="col">{{ obj.objective_name }}</th>
{% endfor %}
<th scope="col">Comments</th>
</tr>
</thead>
<tbody>
<form action="" method="post" class="form-group">
{% csrf_token %}
{% for student in student_list %}
<tr>
<td >{{ student.student_first }} {{ student.student_last }}</td>
{% for g in grade_list %}
{% if g.student.id == student.id %}
<td>
<input type="text" hx-post="{% url 'gradebook:grade-change' g.pk %}" hx-target="" hx-trigger="keyup delay:2s" class="form-control score" title={{ g.score }} name="score" id="input-{{ forloop.counter0 }}" placeholder={{ g.score }} required>
</td>
{% endif %}
{% endfor %}
<td>
{% for comms in comment_list %}
{% if comms.student == student %}
<a class="grade-comment" href="{% url 'gradebook:addcomment' comms.pk assess_pk class_pk %}">{{ comms.comment|truncatewords:10 }}</a>
{% endif %}
{% endfor %}
</td>
</tr>
{% endfor %}
</form>
</tbody>
</table>
EDIT - a solution
I came across this, which works.
<script>
document.body.addEventListener('htmx:configRequest', (event) => {
event.detail.headers['X-CSRFToken'] = '{{ csrf_token }}';
});
</script>
The CSRF token must be included in the HTMX POST request. Currently you include it only in the regular form, but the HTMX request is initiated from a child input element where the token is not present. So just include the following hx-header attribute to a parent element like the <form> or event the <body> is a good candidate:
<body hx-headers='{"X-CSRFToken": "{{ csrf_token }}"}'>

Dynamically update table when creating new enty using HTMX

After recent success in some simple HTMX tasks I wanted to extend adamchainz django-htmx example by a modal field that updates a table dynamically. I am not sure if I am returning the right thing in render ... but my problem is, the table is not being updated. Only when I hit reload.
view:
class CreateProductView(TemplateView):
template_name = "app/product_create.html"
form_class = OrderForm
def get(self, request, *args, **kwargs):
return render(request, self.template_name, {'form': self.form_class()})
def post(self, request):
Product.objects.create(name = request.POST["name"], price = request.POST["price"])
part_template = "app/partial-rendering.html"
return render(request, part_template, {"base_template": "app/_partial.html"})
urls.py:
path("create/", views.CreateProductView.as_view(), name = 'create'),
This is my index.html with the table in it:
<div class="..."><button class="btn btn-primary"
id="showButton"
hx-get="/create"
hx-target="#modal-create">create</button</div>
<main role="main" id="main">{% include "app/orders_table.html" %}</main>
<div id="modal-create"></div>
I also have a partial-rendering.html in place:
{% extends base_template %}
{% block main %}
{% include "app/product_table.html" %}
{% endblock %}
and a _partial.html:
<main id="main">
{% block main %}{% endblock %}
</main>
I will not post the whole product_table.html here, I guess it is straight forward ... mainly:
<table class="table table-striped table-hover">
<thead>
<tr>
<th>product name</th>
<th>price</th>
</tr>
</thead>
<tbody>
{% for product in page.object_list %}
<tr>
<td>{{ product.name}}</td>
<td>{{ product.price }}</td>
</tr>
{% endfor %}
</tbody>
</table>
The data from the form is collected via JS and sent to django using AJAX. I did not use the normal form submit because I wanted to avoid a page reload (which would solve that problem, I know).
I did try to put this together from the example mentioned above. Everything except the dynamic page update runs well!
Some approach is to remove the div targeted:
<div id="modal-create"></div>
then in your table body write the id deleted:
<tbody id="modal-create">
you want some partial for your table loops
{% for product in page.object_list %}
{% include 'some_table_body_tr_partial.html' %}
{% endfor %}
your partial might contain something like this:
<tr hx-target="this" hx-swap="outerHTML">
<td>{{ product.name}}</td>
<td>{{ product.price }}</td>
</tr>
This approach have an issue: the width of the form will take the wide of the first column.

form doesn't submit productbacklogs to database

here is my project code .
I after posting data by form nothing happens.
#model.py
from django.db import models
from projectapp.models import Project
class Productbacklog(models.Model):
project=models.ForeignKey(Project,on_delete=models.CASCADE,default=None)
pbId=models.IntegerField(primary_key=True)
pbTitle=models.CharField(max_length=100)
pbPriority=models.IntegerField(blank=True, null=True)
class Meta:
unique_together=('project','pbId')
def __str__(self):
return self.pbTitle
#forms.py
from django import forms
from productbacklogapp.models import Productbacklog
from projectapp.models import Project
class ProductbacklogForm(forms.ModelForm):
class Meta:
model = Productbacklog
exclude=('pbId','project')
fields=['pbTitle']
#views.py
def productbacklogall(request):
if request.method == 'POST':
form = ProductbacklogForm(request.POST)
if form.is_valid():
form.instance.manage = Project.objects.get_or_create(cname=form.cleaned_data['manage_id'])
form.save()
messages.success(request, ('new productbacklog added'))
return redirect('productbacklogall')
else:
pb_all=Productbacklog.objects.all()
return render(request, 'productbacklogall.html', {'pb_all':pb_all})
I think that issue is on forms.py or views.py but I can't find it.
I'm so greatful if anyone can help me.
here is also my html code,when I submit something the method is post but I don't know why it doesn't go to data basse.
#productbacklogall.html
{%extends 'base.html'%}
{%block title%}
<title>backlog all</title>
{%endblock title%}
{%block content%}
</br>
{% if messages %}
{% for message in messages %}
<div class="alert alert-primary" role="alert">
{{ message }}
×
</div>
{% endfor %}
{% endif %}
<div class="container">
<form method="POST" class="row">
{% csrf_token %}
<label class="col-lg-4"></label>
<input type="text" class="form-control" name="Project" placeholder="project title?"/>
<input type="number" class="form-control" name="pbId" placeholder="backlog id?"/>
<input type="text" class="form-control" name="pbTitle" placeholder="pb title?"/>
<button type="submit" class="btn btn-primary col-lg-2">add project</button>
</form>
</div>
</br>
</br>
<table class="table table-bordered text-center">
<thead class="thead-dark">
<tr>
<th scope="col"> backlog-title</th>
<th scope="col">Edit</th>
<th scope="col">delivarable_task</th>
<th scope="col">related project</th>
</tr>
</thead>
<tbody>
{% load static %}
{% if pb_all %}
{% for obj in pb_all %}
<tr>
<td>{{ obj.pbTitle }}</td>
<td><a href='{% url "delete_productbacklog" obj.pbId %}'>delete</a></td>
<td> <a href=#> delivarabletask </a></td>
<td>{{ obj.project.pbTitle }}</td>
</tr>
{% endfor %}
{% endif %}
</tbody>
</table>
</div>
{%endblock content%}
Your form data are not valid, to see them, add this code to your views :
else:
messages.error(request, "Error")
like this :
def productbacklogall(request):
if request.method == 'POST':
form = ProductbacklogForm(request.POST)
if form.is_valid():
form.instance.manage = Project.objects.get_or_create(cname=form.cleaned_data['manage_id'])
form.save()
messages.success(request, ('new productbacklog added'))
else:
messages.error(request, "Error")
return redirect('productbacklogall')
else:
pb_all=Productbacklog.objects.all()
return render(request, 'productbacklogall.html', {'pb_all':pb_all})

Queryset for current logged in user Django

I am doing queryset with my model. Now the queryset is displaying all the data in my html page. But I want to display only the logged in users data.
models.py
class Data(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
Status = models.CharField(max_length=10, blank=False)
Date = models.DateField(blank=True, null=True)
views.py
#login_required
def search(request):
status_list = Data.objects.all()
status_filter = UserFilter(request.GET, queryset=status_list)
return render(request, 'users/data.html', {'filter': status_filter})
filters.py
class UserFilter(django_filters.FilterSet):
class Meta:
model = Data
fields = {
'Date': ['year','month', ], 'user': ['exact', ],
}
I tried with different views.py also but it didn't work.
#login_required
def search(request, user):
status_list = Data.objects.get(user=self.request.user).search(query)
status_filter = UserFilter(request.GET, queryset=status_list)
return render(request, 'users/data.html', {'filter': status_filter})
data.html
<!DOCTYPE html>
{% load django_tables2 %}
{% load staticfiles %}
<html lang="en" dir="ltr">
<head>
<meta charset="utf-8">
<title></title>
</head>
<body>
{% block content%}
<form method="get">
{{ filter.form.as_table }}
</select>
<button type="submit" class="btn btn-primary">
<span class="glyphicon glyphicon-search"></span> Search
</button>
</form>
<ul>
{% for user in filter.qs %}
<li>{{ user.username }} - {{ user.get_full_name }}</li>
{% endfor %}
</ul>
<table id="datatable" style="margin-top: 20px" style="margin-
bottom:20px" class="table table-bordered" >
<thead>
<tr>
<th>user</th>
<th>EndDate</th>
<th>Status</th>
</tr>
</thead>
<tbody>
{% for Data in filter.qs %}
<tr>
<td>{{ Data.user }}</td>
<td>{{ Data.EndDate }}</td>
<td>{{ Data.Status }}</td>
</tr>
{% empty %}
<tr>
<td colspan="5">No data</td>
</tr>
{% endfor %}
</tbody>
</table>
{% endblock content%}
</body>
Above I added my HTML code also.Please look into html code also.
There could be better ways to do this. As you are still learning below code does the trick.
#login_required
def search(request, *args, **kwargs):
status_list = Data.objects.get(user=request.user)
month = request.GET.get("month", None)
year = request.GET.get("year", None)
if month:
status_list = status_list.filter(Date__month=month)
if year:
status_list = status_list.filter(Date__year=year)
return render(request, 'users/data.html', {'filter': status_list})
You can simplify your code, django provides many helpers function to help to programmer.
in your views.py you can do:
#login_required
def search(request):
status_list = Data.objects.all()
status_filter = status_list.filter(user=request.user) //get current user id
return render(request, 'users/data.html', {'filter': status_filter})
status_list = Data.objects.get(user=self.request.user)
is wrong as self is only used in class based views. No wonder it did not work.
Please try with the below code instead
status_list = Data.objects.filter(user=request.user) or
status_list = Data.objects.get(user=request.user)
So, the final code will be like
#login_required
def search(request):
status_list = Data.objects.get(user=request.user)
render(request, 'users/data.html', {'filter': status_list})
Below code should work fine.
{% for data in filter %}
<li>{{ data.user.username }} - {{ data.user.get_full_name }}</li>
{% endfor %}
{% for Data in filter %}
<tr>
<td>{{ Data.user }}</td>
<td>{{ Data.Date }}</td>
<td>{{ Data.Status }}</td>
</tr>
{% empty %}
As you would not be using filters, so filters.py should be removed.
This will also not work. Make the changes as required.
{{ filter.form.as_table }}

Django formset is not getting saved

I have a formset and I am trying to save it back. But, when I try to check if formset.is_valid() and save, validation always fails even if nothing in the formset has been changed. I am displaying already existing data using formset and trying to edit it. I don't know where I am going wrong. Can someone please help? :(
views.py
def product(request, product_id):
product = get_object_or_404(Product, pk=product_id)
productform = ProductForm(instance=product)
itemformset = modelformset_factory(Item, form = ItemForm, extra=0)
items = itemformset(queryset = Item.objects.filter(Product=product_id), prefix='items', )
if request.method == 'POST':
productform = ProductForm(request.POST, instance=product)
items = itemformset(request.POST, queryset = Item.objects.filter(Product=product_id), prefix='items', )
if productform.is_valid():
productform.save()
if items.is_valid():
items.save()
else:
print("fail")
context = {
'productform':productform,
'product_id': product_id,
'items':items,
}
return render(request, 'product.html', context )
.html
{% bootstrap_form productform %}
<div class="panel panel-default">
<div class="panel-heading">
<h4 class="panel-title">
<a data-toggle="collapse" data-parent="#accordion" href="#Items"> Items List </a>
</h4>
</div>
<div id="Items" class="panel-collapse collapse">
<table class="table table-bordered">
<thead>
<tr>
<th>Name</th>
<th>Tag</th>
</tr>
<thead>
<tbody>
{{ items.management_form }}
{% for form in items %}
<tr>
{{ form.id}}
<td> {{ form.Name }} </td>
<td> {{ form.Tag }} </td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</div>
You are rendering the form fields individually, but not displaying the errors. First, allow Django to render the forms and get the view working. Once you've done that, you can customise the template if required.
<tbody>
{{ items.management_form }}
{% for form in items %}
{{ form }}
{% endfor %}
</tbody>
See the docs on using formsets in templates rendering form fields manually for more information.
Doing it this way should allow you to see errors in the template. Another option is to print or log productform.errors and items.errors. Your current print("fail") tells you there is a problem, but not what the problem is.