Django - Populating Edit/Update Form With an Object's Preexisting Data - python

I have an edit/update values form in Django, in which you need to choose the object you want to change its details from a drop down menu and then enter the new information you wanted to edit/update.
When you choose an object from the dropdown menu the form looks like this:
I am trying to populate the edit form with the pre existing fields of the object that I chose from the dropdown menu to make it easier to make changes.
it should look like this screenshot:
Any ideas how I can achieve that?
Views.py Code:
def tasks(request):
if request.user.is_superuser:
context = {
'tasks':Task.objects.all(),
'title': 'Tasks',
'addForm':AddNewTask(prefix = 'add'),
'editForm':EditTask(prefix = 'edit'),
}
else:
context = {
'tasks':Task.objects.filter(created_by=request.user),
'title': 'Tasks',
'addForm':AddNewTask(prefix = 'add'),
'editForm':EditTask(prefix = 'edit'),
}
if request.method =='POST':
if 'addNew'in request.POST:
addForm = AddNewTask(request.POST,prefix = 'add')
if addForm.is_valid():
task = addForm.save(commit=False)
task.created_by = request.user
task.save()
messages.success(request, f' Task Created Successfully ')
else:
messages.warning(request, 'Something Went Wrong !')
elif 'editExisting' in request.POST:
editForm = EditTask(request.POST,prefix = 'edit')
if editForm.is_valid():
taskID = editForm.cleaned_data['existing_task'].id
new_title = editForm.cleaned_data['title']
new_desc = editForm.cleaned_data['description']
new_status = editForm.cleaned_data['status']
object = Task.objects.get(id=taskID)
object.title = new_title
object.description = new_desc
object.status = new_status
object.save()
messages.success(request, f'Task #{taskID} Has Been Edited Successfully!')
else:
messages.warning(request, 'Something Went Wrong !')
return render(request,'ticketing/tasks.html',context)
forms.py Code:
class EditTask(forms.Form):
existing_task = forms.ModelChoiceField(Task.objects.order_by('id'),label= 'Existing Task')
title = forms.CharField(max_length=100,required=True,label ='Title')
description = forms.CharField(widget=forms.Textarea,label='Description')
status = forms.ChoiceField(choices=Status,label='Change Status')
models.py Code:
class Task(models.Model):
id = models.AutoField(primary_key=True)
title = models.CharField(max_length=100)
description = models.TextField()
last_modified = models.DateTimeField(auto_now=True)
created_at = models.DateTimeField(auto_now_add=True)
created_by = models.ForeignKey(User,on_delete=models.PROTECT)
status = models.CharField(choices=Status,max_length=2,default='O')
def __str__(self):
return str(self.title)

Here's the issue.
You load the page and render the form. Then you choose an object representing a row of data with which to populate the form. At this point the form is already rendered.
You have two options:
Restructure your workflow so that the choice field and the edit form are on separate page loads. You load the dropdown field, choose an object, and submit the form, which then re-renders the form (200 on POST or redirect) with the data populated.
Handle the data through JavaScript on the client side. You will need to watch the dropdown field and then populate the form with data through JavaScript.
A. You can either pass ALL of the data for all of the choices to the JavaScript on the initial page load, which could be a lot of data for a large table, or...
B. You can retrieve the data through an API when the dropdown is changed.
I would favour the first option. Present the user with the dropdown and a next button (/edit/). Use that form data to redirect to a page with the edit form and a save button (/edit/<id>/). Use that form data to update the object and redirect back to the dropdown page (/edit/).

Related

Python, Django, HTMX, is it possible to save data, when a user clicks <button>

When a user click <button> on index page, can save data immediately?
views.py
def index(request):
...
...
context = { ... }
response = render(request, 'docu/index.html', context)
### Save visitors' IP and Keywords to KeyLOG table ###
ip = request.META.get('REMOTE_ADDR')
keywords = request.META.get('HTTP_REFERER', '')
keylog = KeylogForm(request.GET)
keylog = keylog.save(commit=False)
keylog.ipaddr = ip
keylog.keyquery = keywords
keylog.svae()
return response
def keylog(request):
if request.method == "GET":
keylog_list = list(Keylog.objects.all().order_by()\
.values('idpaddr', 'keywords', 'tel_log', 'regtime'))
return JsonResponse(keylog_list, safe=False)
return JsonResponse({'message':'error'})
forms.py
class KeylogForm(forms.ModelForm):
class Meta:
model = Keylog
fields = ['ipaddr','keyquery','tel_log']
models.py
class Keylog(models.Model):
ipaddr = models.CharField(max_length=32,null=True, blank=True) #save IP addr
keyquery = models.CharField(max_length=128,null=True, blank=True) #save keywords
tel_log = models.CharField(max_length=256,null=True, blank=True) #save tel_log
regtime = models.DateTimeField(auto_now_add=True) #created time
Currently, it makes table as below;
Ipaddr
keyquery
tel_log
regtime
192.168.x.yy
?query=apple
2023.01.01.12.24.56
192.168.a.bb
?query=banana
2023.01.01.11.22.33
I hope to save Tel_log data in the above table, when a user clicks <butto>...</button>.
So I made an index.html as below;
index.html
<div id="tel_log">
<button class="btn btn-danger">
hx-get="keylog/"
hx-vals="[tel_log='CALL']"
hx-trigger="click">
Phone NUMBER
</button>
</div>
My expectation is that when I clicked [Phone NUMBER] on the index page, "CALL" is appeared on the "tel_log" column of the above table, but it didn't work, just shows {message:error}
I'm a newbie on programming, so my approach for my needs may be wrong, but I'm really eager to make a web site for now, especially on this question. I also did try to use Ajax, but I didn't get any response also.
Thank you for all.
Click [button], save data in real time on the column of the table.

Why does my Submit button renders a page that is blank when it is supposed to contain the data that was just updated?

I'm trying to update the values of my database using a HTML Form.
When I Click Edit it brings me to the edit the values above.
However as I am clicking the submit button, it returns me a database but with no other values.
Is there anyone that can help me understand what I did wrong and point me to the right documentation (if any)
editclaims.html:
<div class="arrange2">
<h1>Edit Claim Form - #{{claims.id}} </h1>
</div>
<form method="POST" action="/update/{{claims.id}}">
{% csrf_token %}
views.py:
def editclaims(request,id):
context = initialize_context(request)
user = context['user']
claims = SaveClaimForm.objects.get(id=id)
if request.method == 'POST':
name = request.POST['name']
email = request.POST['email']
claim = request.POST['claim']
claimtype = request.POST.get('claimtype')
description = request.POST['description']
receipt = request.FILES['receipt']
cheque = request.POST.get('Cheque')
form = SaveClaimForm(name=name, email=email, claim=claim, claimtype=claimtype, description=description, receipt=receipt, cheque=cheque)
form.save()
return render(request, "Login/editclaims.html", {'claims':claims, 'user':user})
urls.py:
urlpatterns = [
path('existingclaims/', views.viewclaims, name='existingclaims'),
path('editclaims/<int:id>', views.editclaims, name='editclaims'),
path('update/<int:id>', views.updateclaims, name='updateclaims'),
]
It may not resolve all your problems but it will be more readable as answer.
When you get data from HTML then you create new object SaveClaimForm and it will have new ID and you will have the same object in two rows.
You have to get original Claim from database and update values in this object and save it - and then it will save it with original ID and you will have only one `object in database
def editclaims(request,id):
context = initialize_context(request)
user = context['user']
# get original object
claims = SaveClaimForm.objects.get(id=id)
if request.method == 'POST':
# update original object
claims.name = request.POST['name']
claims.email = request.POST['email']
claims.claim = request.POST['claim']
claims.claimtype = request.POST.get('claimtype')
claims.description = request.POST['description']
claims.receipt = request.FILES['receipt']
claims.cheque = request.POST.get('Cheque')
# save it with original `ID`
claims.save()
return render(request, "Login/editclaims.html", {'claims':claims, 'user':user})
BTW:
Django has special class ModelForm to create forms in HTML. It may also have methods to check if data in HTML are correct - ie. if fields are not empty, if email is correctly constructed (name#domain.com), if phone has only numbers, etc. So using ModelForm can be more useful then writing all manually in code.

Passing table values from template to view

I have template with table which is dynamically created by javascript. Then I want retrieve with submit button only value within <td name="plu"></td> tags and process it in view to save as json into CharField in model. I tried to use form tag with method post and request.POST.get('plu') in view but it won't work. I want to get that values and then process it with model methods.
Here are parts of code:
#login_required()
def cash_register(request):
"""plus = [123,345,567]
newtrans = Transaction()
newtrans.set_plu_list(plus)
newtrans.save()"""
if request.method == 'POST':
x = request.POST.get('plu', False)
# ???
try:
products = Product.objects.all().values_list('plu_num', 'art_name', 'sales_price_brutto')
products_json = json.dumps(list(products), cls=DjangoJSONEncoder)
except Product.DoesNotExist:
raise Http404('Products cant\'t be found!')
return render(request, 'panel/cash_register.html', {'products_json': products_json})
class Transaction(models.Model):
plu_list = models.CharField(max_length=255)
transaction_date = models.DateField(auto_now_add=True)
is_ksk = models.BooleanField(default=False)
ksk_num = models.IntegerField(null=True)
def set_plu_list(self, x):
self.plu_list = json.dumps(x)
def get_plu_list(self):
return json.loads(self.plu_list)
And html: https://pastebin.com/RFn5qNn2
This isn't a thing you can do. <td> elements are not form fields; you can't "submit" them.
If you want values to be submitted to the backend, you either have to use actual form inputs, or use Ajax to send the data.

Get Django Auth "User" id upon Form Submission

I currently have a model form that submits an entered domain to the db.
The problem I'm encountering is, I need to save the currently logged in user's ID (PK from the django.auth table) when a domain is submitted to satisfy a PK-FK relationship on the db end.
I currently have:
class SubmitDomain(ModelForm):
domainNm = forms.CharField(initial=u'Enter your domain', label='')
FKtoClient = User.<something>
class Meta:
model = Tld #Create form based off Model for Tld
fields = ['domainNm']
def clean_domainNm(self):
cleanedDomainName = self.cleaned_data.get('domainNm')
if Tld.objects.filter(domainNm=cleanedDomainName).exists():
errorMsg = u"Sorry that domain is not available."
raise ValidationError(errorMsg)
else:
return cleanedDomainName
and views.py
def AccountHome(request):
if request.user.is_anonymous():
return HttpResponseRedirect('/Login/')
form = SubmitDomain(request.POST or None) # A form bound to the POST data
if request.method == 'POST': # If the form has been submitted...
if form.is_valid(): # If form input passes initial validation...
domainNmCleaned = form.cleaned_data['domainNm'] ## clean data in dictionary
clientFKId = request.user.id
form.save() #save cleaned data to the db from dictionary`
try:
return HttpResponseRedirect('/Processscan/?domainNm=' + domainNmCleaned)
except:
raise ValidationError(('Invalid request'), code='300') ## [ TODO ]: add a custom error page here.
else:
form = SubmitDomain()
tld_set = request.user.tld_set.all()
return render(request, 'VA/account/accounthome.html', {
'tld_set':tld_set, 'form' : form
})
The problem is it gives me an error of: (1048, "Column 'FKtoClient_id' cannot be null"), very odd thing happening, for the column FKtoClient, its trying to submit: 7L instead of 7(the PK of this user's record). Any ideas?
If someone can please help, I would really appreciate it
Firstly, remove FKtoClient from your form. You need to set the user in your view where you can yes the request object. It's not possible to set an attribute on the form that automatically sets the current user.
When instantiating your form, you can pass a tld instance which already has the user set.
def AccountHome(request):
# I recommend using the login required decorator instead but this is ok
if request.user.is_anonymous():
return HttpResponseRedirect('/Login/')
# create a tld instance for the form, with the user set
tld = Tld(FKtoClient=request.user)
form = SubmitDomain(data=request.POST or None, instance=tld) # A form bound to the POST data, using the tld instance
if request.method == 'POST': # If the form has been submitted...
if form.is_valid(): # If form input passes initial validation...
domainNm = form.cleaned_data['domainNm']
form.save() #save cleaned data to the db from dictionary
# don't use a try..except block here, it shouldn't raise an exception
return HttpResponseRedirect('/Processscan/?domainNm=%s' % domainNm)
# No need to create another form here, because you are using the request.POST or None trick
# else:
# form = SubmitDomain()
tld_set = request.user.tld_set.all()
return render(request, 'VA/account/accounthome.html', {
'tld_set':tld_set, 'form' : form
})
This has an advantage over #dm03514's answer, which is that you can access the user within form methods as self.instance.user if required.
If you want to Require that a user be logged in to submit a form, you could do something like:
#login_required # if a user iS REQUIRED to be logged in to save a form
def your_view(request):
form = SubmitDomain(request.POST)
if form.is_valid():
new_submit = form.save(commit=False)
new_submit.your_user_field = request.user
new_submit.save()
You can get the logged in user from the request object:
current_user = request.user

Filtering data with __range

I'm using django_tables2 thus part of the code is dependent on this package, but this should be irrelevant to the overall problem.
forms.py
class PersonForm(forms.Form):
date_from = forms.DateField(input_formats='%d/%m/%Y')
date_to = forms.DateField(input_formats='%d/%m/%Y')
views.py
def filter(request):
table = PersonTable(Person.objects.all())
if request.method == 'POST':
form = PersonForm(request.POST)
if form.is_valid():
date_from = form.cleaned_data['date_from']
date_to = form.cleaned_data['date_to']
result_filtered = table.filter(date__range=(date_from, date_to))
RequestConfig(request, paginate={"per_page": 100}).configure(table)
return render(request, "people.html", {
"table": result_filtered })
args = {}
args.update(csrf(request))
args['form'] = PersonForm()
args['table'] = table
RequestConfig(request, paginate={"per_page": 100}).configure(table)
return render(request, 'people.html', args)
Simply, filtering is not working. I can see the entire table, but when I try to filter nothing happens. Can you see what's wrong?
I'm pretty sure you need to call .filter() on the query set rather than the table. For example:
result_filtered = PersonTable(Person.objects.filter(date__range=(date_from, date_to))
Also, on this line:
RequestConfig(request, paginate={"per_page": 100}).configure(table)
You are passing in table. You should pass in result_filtered instead.
This is one way I'd do it, assuming your Person model has a date field:
def filter(request):
if 'date_from' in request.GET and 'date_to' in request.GET:
# form data has been submitted
form = PersonForm(request.GET)
if form.is_valid():
date_from = form.cleaned_data['date_from']
date_to = form.cleaned_data['date_to']
people = Person.objects.filter(date__range=(date_from, date_to))
table = PersonTable(people)
else:
table = PersonTable(Person.objects.all())
else:
form = PersonForm()
table = PersonTable(Person.objects.all())
RequestConfig(request, paginate={"per_page": 100}).configure(table)
args = {}
args.update(csrf(request))
args['form'] = form
args['table'] = table
return render(request, 'people.html', args)
This binds the form if both its expected fields are present, and limits the queryset according to the results if valid. If not valid, it renders the bound form and the table built from the unlimited table. If form data was not submitted, it renders the table built from the unlimited table.
Your form tag's method attribute should be GET rather than POST, if using this design.
This doesn't quite follow the usual Django patterns for form handling because the form isn't actually making any changes, so you can use GET and don't need to return a redirect on success.

Categories

Resources