Form not submitting to the DataBase when using HTMX - python

I have the following models, and as you can see they are related to each other
class Leads(models.Model):
project_id = models.BigAutoField(primary_key=True, serialize=False)
created_at = models.DateTimeField(auto_now_add=True)
expected_revenue = MoneyField(decimal_places=2,max_digits=14, default_currency='USD')
expected_licenses = models.IntegerField()
country = CountryField(blank_label='(select_country)')
status = models.CharField(choices=[('Open', 'Open'), ('Closed', 'Closed'), ('Canceled', 'Canceled'),
('Idle', 'Idle')
], max_length=10)
estimated_closing_date = models.DateField()
services = models.CharField(choices=[('Illumination Studies', 'Illumination Studies'),
('Training', 'Training'),('Survey Design Consultancy', 'Survey Design Consultancy'),
('Software License', 'Software License'),
('Software Development','Software Development')], max_length=40)
agent = models.ForeignKey(Profile, default='agent',on_delete=models.CASCADE)
company = models.ForeignKey(Company,on_delete=models.CASCADE)
point_of_contact = models.ForeignKey(Client, default='agent',on_delete=models.CASCADE)
updated_at = models.DateTimeField(auto_now=True)
application = models.CharField(choices=[('O&G','O&G'),('Renewables','Renewables'),('Mining','Mining'),
('Other','Other'),('CSS','CSS')],
default='O&G',max_length=20)
sub_category = models.CharField(choices=[('Wind','Wind'),('Geo-Thermal','Geo-Thermal'),('Solar','Solar'),
('Tidal','Tidal')], max_length=20, blank=True)
#property
def age_in_days(self):
today = date.today()
result = self.estimated_closing_date - today
return result.days
def __str__(self):
return f'{self.project_id}'
class LeadEntry(models.Model):
revenue = MoneyField(decimal_places=2,max_digits=14, default_currency='USD',blank=True)
date = models.DateField()
lead_id = models.ForeignKey(Leads,on_delete=models.CASCADE)
id = models.BigAutoField(primary_key=True, serialize=False)
probability = models.DecimalField(max_digits=2, decimal_places=2, default=0, blank=True)
#property
def est_revenue(self):
result = self.revenue * probabiity
return result
Essentially a lead entry is related to the lead, and I'm using HTMX to essentially add data to LeadEntry database using a form.
Form
class LeadEntryForm(forms.ModelForm):
class Meta:
model = LeadEntry
fields = ('lead_id','date','revenue','probability')
widgets = {'date': DateInput()}
I have 2 views, one that will simply pass an HTML with a button for the user to add entries to the database, and another view with the actual form
views
#login_required
def add_to_forecast(request):
id = request.GET.get('project_id')
request.session['lead_id'] = id
return render(request, 'account/lead_forecast.html')
#login_required
def forecast_form(request):
if request.method=='POST':
form = LeadEntryForm(data=request.POST or None)
if form.is_valid():
form.save(commit=False)
messages.success(request,"Successful Submission")
return redirect('add_to_forecast')
lead_id = request.session['lead_id']
data = {'lead_id':lead_id}
context = {
"form":LeadEntryForm(initial=data)
}
return render(request, 'account/lead_entry_forecast.html', context)
Lastly, there are 2 HTML pages, the first one is associated with add_to_forecast and renders a simple page, this is where the HTMX happens and this adds the form from the next HTML page
{% extends "base.html" %}
{% load crispy_forms_tags %}
{% load static %}
{% block title %}Client Information {% endblock %}
{% block content %}
<h1> Add Lead to Sales Forecast </h1>
<p>Click the Button below to add a Payment Date</p>
<button type="button" hx-get="{% url 'lead_entry_forecast' %}" hx-target="#leadform" hx-swap="beforeend" >
Add Lead </button>
<div id="leadform">
<br>
</div>
{% endblock %}
The form that is added by the user as many times they want to do so
{% load crispy_forms_tags %}
{% load static %}
{% block content %}
<div class="container-fluid">
<form method="post" enctype="multipart/form-data" action=".">
{% csrf_token %}
<div class="row justify-content-center">
<div class="col-sm-1">
{{ form.lead_id|as_crispy_field }}
</div>
<div class="col-sm-2">
{{ form.date|as_crispy_field }}
</div>
<div class="col-sm-2">
{{ form.revenue|as_crispy_field }}
</div>
<div class="col-sm-1">
{{ form.probability|as_crispy_field }}
</div>
<div class="col-sm">
<input type="submit" value="Submit" >
</div>
</div>
</form>
</div>
<hr>
{% endblock %}
The problem I have is that after submitting the form, everything seems to be working, obviously, at the moment you can only submit one lead, as I still have to add more logic, but at the moment the form is not sending data to the DB, after some debugging, I was not able to pass the POST, it seems like is just not submitting anything.
Any help will be highly appreciated
Update - 12/4/2021
I have tried to submit data using the form and it actually works, so there is nothing wrong with the form submitted the POST.request, if I go directly to the URL, it works fine, so the form is submitted correctly and the database is updated.
I still don't understand why is not working when the form is rendered by the htmx, so it has something to do with that.

When you are designing an HTMX page, you have to be careful with the post request, so I was processing the form in the wrong view, please see below the views:
#login_required
def add_to_forecast(request):
form = LeadEntryForm(data=request.POST or None)
if request.method == 'POST':
if form.is_valid():
form.save()
return HttpResponse("Success")
else:
return render(request, 'account/lead_entry_forecast.html', {"form": form})
id = request.GET.get('project_id')
request.session['lead_id'] = id
return render(request, 'account/lead_forecast.html')
#login_required
def forecast_form(request):
lead_id = request.session['lead_id']
data = {'lead_id':lead_id}
context = {
"form":LeadEntryForm(initial=data)
}
return render(request, 'account/lead_entry_forecast.html', context)
Now it works as it should

Related

(Django) Can't Update Post On My Blog (function based view)

After displaying my posts, I don't manage to Edit any of them.
When I print the instance variable which is in views.py in my terminal, it displays only the title and the author like this title - author, which is the method defined in models.py.
Help please!
Views.py
#login_required(login_url='login_view')
def update_post_view(request, post_id, slug=None):
instance = Article.objects.get(id = post_id)
if request.method == 'POST':
form = UpdatePostForm(request.POST, request.FILES, instance=instance)
if form.is_valid():
form.save()
return redirect('posts_view')
else:
form = UpdatePostForm(instance=instance)
return render(request,'update_post.html', {'form':form})
forms.py
class UpdatePostForm(forms.ModelForm):
class Meta:
model = Article
fields = ('author',)
title = forms.CharField(max_length=255, label='username',
widget= forms.TextInput(attrs= {'placeholder':'Title...', 'class': 'title'}))
body = forms.CharField(max_length=255, label='body', required=True, widget=forms.Textarea(attrs={'placeholder':'Start writing your post...'}))
urls.py
path('update_post/<int:post_id>/<slug:slug>', views.update_post_view, name='update_post_view')
update_post.html
{% extends 'base.html' %}
{% block title %}Update Post{% endblock %}
{% block content %}
<h2>Update Posts...</h2>
<form action="" method="POST">
{% csrf_token %}
<div>
<h3>{{form.title}}</h3>
<small>{{form.author}}</small>
<p>{{form.body}}</p>
</div>
<button class="btn btn-secondary">Update</button>
</form>
{% endblock %}

Django displaying each object in a button

Good day, I have a page to test where tickets are created and where all created tickets (titles) are listed. Now you should be able to click on the title of a created ticket.
The button then redirects to an extra page where all the information of the specific ticket is.
I've always had trouble displaying user specific stuff, so what needs to be done so that the href="" of each link redirects in HTML to the specific ticket?
I did the following
\\forms.py
{% for i in request.user.addtitle_set.all %}
<div>
{% if i.user == request.user %} {{ i.title }} {% endif %}
<form action="{% url SOMETHING_HERE i.id}" style="display: inline">
<button type="submit">View Ticket</button>
</form>
</div>
<br />
{% endfor %}
\\urls.py
path('dashboard/user/ticket', ticket_view),
path('dashboard/user/ticket/<int:id>/',
ticket_system_view, name="view_ticket"),
\\ models.py
class Ticket(models.Model):
user = models.ForeignKey(
settings.AUTH_USER_MODEL,
default=None,
null=True,
on_delete=models.CASCADE,
)
title = models.CharField(max_length=200)
description = models.TextField()
creator_adress = models.GenericIPAddressField(null=True)
start_date = models.DateTimeField(default=timezone.now)
def __str__(self):
return str(self.title)
\\views.py
#login_required
def ticket_view(request, id, *args, **kwargs):
try:
obj = Ticket.objects.get(id=id)
except Ticket.DoesNotExist:
return redirect('/')
if request.method == 'POST':
form = TicketForm(request.POST, instance=Ticket(user=request.user))
if form.is_valid():
obj = form.save(commit=False)
obj.creator_adress = get_client_ip(request)
obj.save()
return redirect('/dashboard/user/ticket')
else:
form = TicketForm()
return render(request, 'ticket.html', {'form': form})
def ticket_system_view(request, id, *args, **kwargsw):
# Here I will put all the stuff for the specific ticket page
return render(request, 'ticket_system.html', {})
Edit:
I can not even access the page because of the error
TypeError at /dashboard/user/ticket
ticket_views() missing 1 required positional argument: 'id'
Edit 2.0:
Solution:
path('dashboard/user/ticket/', ticket_view),
path('dashboard/user/ticket/<int:id>/',
ticket_system_view, name="view_ticket"),
{% for i in request.user.ticket_set.all %}
<a href="{% url 'view_ticket' i.id %}"
>{% if i.user == request.user %} {{ i.title }} {% endif %}</a
>
<br />
{% endfor %}
Your urls.py
path('dashboard/user/ticket', ticket_view),
path('dashboard/user/ticket/<int:id>/',
ticket_system_view, name="view_ticket"),
Should be
path('dashboard/user/ticket/<int:id>', ticket_view, name="some_name"),
path('dashboard/user/ticket/<int:id>/',
ticket_system_view, name="view_ticket"),
As ticket_view expects an id
ticket_views() missing 1 required positional argument: 'id'
In your views.py you are getting argument id so it must be passed.
Change your urls.py as
path('dashboard/user/ticket/<int:pk>', ticket_view, name='ticket_view'),
path('dashboard/user/ticket/<int:id>/',
ticket_system_view, name="view_ticket"),
Also your urlnamespace in your form as
<form action="{% url 'ticket_view' i.id %}" style="display: inline">
There must be spaces before and after % in your url template tag. You were missing ending % add that too.

Django - objects.all() shows nothing

I'm trying to get a list of objects in Django from a model.
I just want to get the list of 'dht node' from the request user, but it shows nothing in the html file (as if the list was empty). The user that I'm using has 2 'dht nodes' and they're shown in the django admin.
I don't know what is wrong, because if I use the instruction "member.dht.create(...)" in the views function and a create a new 'dht node' like this, this is shown. Only 'dht nodes' that I enter by form do not show. Can be the form?
Thanks a lot, Here's my code:
Models.py
class Node(models.Model):
name = models.CharField(primary_key=True, null=False, max_length= 50)
description= models.CharField(default=None, null=False, max_length= 250)
topic=models.CharField(default=None, null=False, max_length= 50, unique=True)
def __unicode__(self):
return self.name
class dht(Node):
temp = models.IntegerField(default=None, null=True)
hum = models.IntegerField(default=None, null=True)
class UserProfile(User):
uid = models.CharField(default=None, null=False, max_length= 250)
dht = models.ManyToManyField(dht, blank=True)
def __unicode__(self):
return self.user.username
Views.py -dht list-
#login_required(login_url = '/web/login')
def listDhtSensor(request):
member = request.user.userprofile
list = member.dht.all()
return render(request, 'web/listDhtSensor.html', {'list':list})
Html -listDhtSensor.html-
{% block content %}
{% for dht in list %}
{{ dht.name }}
{{ dht.topic }}
{% endfor %}
{% endblock %}
Forms.py
class newDHTSensorForm(forms.ModelForm):
class Meta:
model = dht
field = ['name',
'description',
'topic',]
labels = {'name': 'Name' ,
'description': 'Description',
'topic': 'Topic',}
exclude = ['temp', 'hum']
Views.py -dht form-
#login_required(login_url = '/web/login')
def newDHTSensor(request):
if request.method == "POST":
form = newDHTSensorForm(request.POST)
if form.is_valid():
post = form.save(commit=False)
post.save()
return redirect('/web/dhtDetail')
else:
form = newDHTSensorForm()
return render(request, 'web/newDhtSensor.html', {'form': form})
Html -newDhtSensor.html-
{% block content %}
<div class="boxReg">
<form method="post">
{% csrf_token %}
<h2>{{ form.name.errors.as_text }}</h2>
<p><label for="id_name">Name:</label> <input class="barraForm" type="text" name="name" maxlength="150" autofocus="" required="" id="id_name"></p>
<p><label for="id_description">Description:</label> <input class="barraForm" type="text" name="description" maxlength="150" id="id_description"></p>
<h2>{{ form.topic.errors.as_text }}</h2>
<p><label for="id_topic">Topic:</label> <input class="barraForm" type="text" name="topic" maxlength="254" id="id_topic"></p>
<div class="boxButtonReg">
<button class="buttonReg" type="submit">Save</button>
</div>
</form>
</div>
{% endblock %}
It shows nothing because you did not link you dht objects to that UserProfile, so if you later query for the dhts for that UserProfile, evidently the list is empty. You should add it to the dht relation, like:
#login_required(login_url = '/web/login')
def newDHTSensor(request):
if request.method == "POST":
form = newDHTSensorForm(request.POST)
if form.is_valid():
post = form.save()
request.user.userprofile.dht.add(post)
return redirect('/web/dhtDetail')
else:
form = newDHTSensorForm()
return render(request, 'web/newDhtSensor.html', {'form': form})
Note that you first need to save the post, so you should omit the commit=False aprameter.

How to redirect to a form after parsing an uploaded csv file if items don't exist in database?

I am having an issue that i have been trying to solve for a few days and feel like I have hit a roadblock and can't find the answers I need. What I am trying to do is:
Upload a csv file (this file basically contains details about stock trades) which will be saved in a table and stored in media directory, the content is then parsed and passed to a dictionary (this step is working) -- the contents of this file will be used to update various tables in my database later.
As I loop through the key, value pairs in the dictionary check to see if certain values exist in various models which are used as foreign keys (this part also works) -- So for example, if a trade is tagged with Fund X and fund X doesn't exist in my Fund table, it will take me to a form to fill in all the required fields to create fund X in my database
If a value is missing I want to be sent to a form that will then allow me to create this value in the database so that I can populate the required fields (this part is partially working, I can get to the form for missing data but I am unable to save the inputs of that form to the data base)
I have a utils.py file that will be containing all of my classes or helper functions to accomplish this.
if i print out fund_form.errors in from my views.py file i get the following:
<ul class="errorlist"><li>fund_code<ul class="errorlist"><li>This field is required.</li></ul></li><li>fund_name<ul class="errorlist"><li>This field is required.</li></ul></li></ul>
Please let me know if you need any other information I have been struggling with this for a few days.
models.py
class Funds(models.Model):
fund_code = models.CharField(max_length=10, primary_key=True)
fund_name = models.CharField(max_length=100, unique=True)
def __str__(self):
return self.fund_code + ' - ' + self.fund_name
class Trades(models.Model):
trade_date = models.DateField(default=timezone.now)
trade_file = models.FileField(upload_to='trades/')
user = models.ForeignKey(User, on_delete=models.PROTECT)
views.py
VALID_FILE_TYPES = ['csv']
def upload_trades(request):
if not request.user.is_authenticated:
return render(request, 'position_mgmt/login.html')
else:
form = UploadFileForm(request.POST or None, request.FILES or None)
if form.is_valid():
trades = form.save(commit=False)
trades.user = request.user
trades.trade_date = request.POST['trade_date']
trades.trade_file = request.FILES['trade_file']
file_type = trades.trade_file.url.split('.')[-1]
file_type = file_type.lower()
if file_type not in VALID_FILE_TYPES:
context = {
'trades': trades,
'form': form,
'error_message': 'File type must be .csv',
}
return render(request, 'position_mgmt/upload_trades.html', context)
trades.save()
trade_upload = ProcessUpload(trades.trade_file.name)
parsed_trades = trade_upload.parse_trades()
database_check = trade_upload.check_database(parsed_trades, request)
if database_check['fund_code']:
fund_form = CreateFundForm(request.POST or None)
database_check['form'] = fund_form
if request.method == 'POST':
if fund_form.is_valid():
fund = fund_form.save(commit=False)
fund.user = request.user
fund.fund_code = request.POST['fund_code']
fund.fund_name = request.POST['fund_name']
fund.save()
return render(request, 'position_mgmt/upload_trades.html', {'trades': trades})
return render(request, 'position_mgmt/positions_form.html', database_check)
return render(request, 'position_mgmt/trade_files.html', {'trades': trades})
context = {'form': form}
return render(request, 'position_mgmt/upload_trades.html', context)
forms.py
class UploadFileForm(forms.ModelForm):
class Meta:
model = Trades
fields = ['trade_date', 'trade_file']
class CreateFundForm(forms.ModelForm):
class Meta:
model = Funds
fields = ['fund_code', 'fund_name']
utils.py
class ProcessUpload:
"""
A class to upload and process trade files
Attributes
----------
name: str
name of the file that will be processed
Methods
-------
parse_trades()
imports csv file from the MEDIA_ROOT and returns a list of dictionaries of format:
[{:,:}, {:,:}......]
check_database()
...
"""
def __init__(self, name):
"""
:param name:
name of the csv file
"""
self.name = name
def parse_trades(self):
file_path = os.path.join(settings.MEDIA_ROOT, self.name)
with open(file_path) as f:
trade_file = csv.DictReader(f, restkey='tags')
return list(trade_file)
def check_database(self, trade_dict, request):
for each_trade in trade_dict:
for _key in each_trade:
if _key == 'fund_code':
if not Funds.objects.filter(fund_code=each_trade[_key]).exists():
return {'fund_code': each_trade[_key],
'error_message': 'fund {0} does not exist'.format(each_trade[_key])}
return None
upload_trades.html
{% extends 'position_mgmt/base2.html' %}
{% block title %}Upload{% endblock %}
{% block body %}
<body>
{% block sub_body %}
{% endblock %}
<br>
<div class="container-fluid">
<div class="row">
<div class="col-sm-12 col-md-7">
<div class="panel panel-default">
<div class="panel-body">
<h3>Upload Trades</h3>
{% if error_message %}
<p><strong>{{ error_message }}</strong></p>
{% endif %}
<form class="form-horizontal" role="form" action="" method="post" enctype="multipart/form-data">
{% csrf_token %}
{% include 'position_mgmt/form-template.html' %}
<div class="form-group">
<div class="col-sm-offset-2 col-sm-10">
<button type="submit" class="btn btn-success">Submit</button>
</div>
</div>
</form>
</div>
</div>
</div>
</div>
</div>
</body>
{% endblock %}
form-template.html
{% for field in form %}
<div class="form-group">
<div class="col-sm-offset-2 col-sm-10">
<span class="text-danger small">{{ field.errors }}</span>
</div>
<label class="control-label col-sm-4">{{ field.label_tag }}</label>
<div class="col-sm-8">{{ field }}</div>
</div>
{% endfor %}

python django: error using inline formset

I have the following models:
class Equipment(models.Model):
asset_number = models.CharField(max_length = 200)
serial_number = models.CharField(max_length = 200)
class Calibration(models.Model):
cal_asset = models.ForeignKey(Equipment, on_delete = models.CASCADE)
cal_by = models.CharField(max_length = 200)
cal_date = models.DateField()
notes = models.TextField(max_length = 200)
and view:
def default_detail (request, equipment_id):
equipment = Equipment.objects.get(id = equipment_id)
if request.method == "POST":
if 'calibration' in request.POST:
EquipmentInlineFormSet = inlineformset_factory(Equipment, Calibration, fields = ('cal_by', 'cal_dates', 'notes')
formset = EquipmentInlineFormSet(request.POST, request.FILES, instance=equipment)
if formset.is_valid():
formset.save()
return HttpResponseRedirect(reverse('calbase:default_detail', args=(post.id)))
else:
formset = EquipmentInlineFormSet(instance=equipment)
return render(request, 'calbase/default_detail.html', {'formset' : formset})
and template for this view:
<h1>{{ equipment.serial_number }}</h1>
{{equipment.serial_number}} -- {{equipment.asset_number}} <br>
calibration history:
{% for calibrations in equipment.calibration_set.all %}
<ul>
<li>
{{calibrations.cal_by}} -- {{calibrations.cal_date}} -- {{calibrations.notes}}
</li>
</ul>
{% endfor %}
{% if error_message %}<p><strong>{{ error_message }}</strong></p>{% endif %}
<form method="POST" action = "{% url 'calbase:default_detail' equipment.id %}">{% csrf_token %}
{{ formset }}
<button type="submit" class="save btn btn-default" name = "calibration">Save</button>
</form>
Back?
This view is simply a detail view of equipment, showing some information (asset and serial # of this piece of equipment). I am trying to add a formset that let user add calibration record to this equipment in this view and display them if any. I learned that using inline formset is probably the way to go. However, after following documentation step by step, I am having a
equipmentInlineFormSet = EquipmentInlineFormSet(request.POST, request.FILES, instance=equipment)
SyntaxError: invalid syntax
error even though I checked to make sure that there is no typos or so. I am just trying to figure out what I did wrong here.
Isn't it a missing closing parenthesis on this line?
EquipmentInlineFormSet = inlineformset_factory(Equipment, Calibration, fields = ('cal_by', 'cal_dates', 'notes')

Categories