python django: error using inline formset - python

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')

Related

Form not submitting to the DataBase when using HTMX

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

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.

Answer create view is not storing data in the database in django

I am working on a Hybrid of Quora and StackOverflow clone. I have made an "add_answer" view for letting me add answers to the questions but for some reasons it is showing the integrity error:
Here's the error image
But when I add through admin panel then it adds answer in the database. What is wrong?
The code is as follows:
models.py:
class Answer(models.Model):
content = models.TextField()
user = models.ForeignKey(User,on_delete=models.CASCADE)
question = models.ForeignKey(Question,on_delete=models.CASCADE,blank=False)
created = models.DateTimeField(auto_now_add=True)
upvotes = models.PositiveIntegerField(default=0)
is_active = models.BooleanField(default=True)
def __str__(self):
return '{}\'s Answer'.format(self.user.username)
class Meta:
ordering = ('-upvotes','-created')
forms.py:
class AnswerForm(forms.ModelForm):
content = forms.CharField(widget=forms.Textarea,help_text='Your Answer in Detail. Note: MarkDown is enabled.')
class Meta:
model = Answer
fields = ['content']
def __init__(self,author,question,*args,**kwargs):
super().__init__(*args,**kwargs)
self.user = author
self.question = question
"add_answer" view (views.py):
#login_required
def add_answer(request, pk):
ques = get_object_or_404(Question, pk = pk)
if request.method == 'POST':
form = AnswerForm(request.user,ques,request.POST)
if form.is_valid():
cd = form.cleaned_data
answer = form.save(commit=False)
answer.content = cd['content']
answer.save()
messages.success(request,'Success! Your Answer has been added!')
return redirect('forum')
else:
messages.error(request,form.errors)
else:
form = AnswerForm(request.user,ques)
return render(request,'discussion/answer_create.html',{'form':form})
answer_create.html (template):
{% extends 'base.html' %}
{% load markdownify %}
{% block title %} Add Answer {% endblock %}
{% block header %}
<h2 class="display-5" style="font-family: x-locale-heading-primary,zillaslab,Palatino,Palatino Linotype,x-locale-heading-secondary,serif;">
Add Answer:
</h2>
{% endblock %}
{% block content %}
{% if form.errors %}
<h4 class="alert alert-primary alert-warning" role="alert">Errors:<br> {{form.errors}} <hr></h4>
{% endif %}
<form action="" method="POST">
{% csrf_token %}
{{form.as_p}}
<input type="submit">
</form>
{% endblock %}
It would be really grateful if somebody helps me out.
You forgot to assign the question instance to answer model
answer.question = ques
ques = get_object_or_404(Question, pk = pk)
if form.is_valid():
cd = form.cleaned_data
answer = form.save(commit=False)
answer.question = ques
answer.content = cd['content']
answer.save()
you can also do the following in model form init
def __init__(self,author,question,*args,**kwargs):
super().__init__(*args,**kwargs)
self.form.instance.user = author
self.form.instance.question = question

Django Form Field not showing up

I just started learning Django and for this project I'm following the "Tango with Django" tutorial book. I have a problem with the input field of a form not showing up, while the button seems to be rendered fine.
Here's my code:
models.py
[...]
class Idea(models.Model):
keyword = models.ForeignKey(Keyword)
word = models.CharField(max_length=120)
count = models.IntegerField(default=1)
def __str__(self):
return self.word
forms.py
[...]
class Meta:
model = Keyword
fields = ('name',)
class IdeaForm(forms.ModelForm):
word = forms.CharField(max_length=120)
count = forms.IntegerField(widget=forms.HiddenInput(), initial=1)
class Meta:
model = Idea
fields = ('word',)
exclude = ('keyword',)
views.py
[...]
def keyword_detail(request, keyword_name_slug):
form = IdeaForm()
context_dict = {}
try:
keyword = Keyword.objects.get(slug=keyword_name_slug)
ideas = Idea.objects.filter(keyword=keyword)
context_dict['keyword'] = keyword
context_dict['ideas'] = ideas
except Keyword.DoesNotExist:
context_dict['keyword'] = None
context_dict['ideas'] = None
if request.method == 'POST':
form = IdeaForm(request.POST)
if form.is_valid():
idea = form.save(commit=False)
idea.keyword = keyword
idea.count = 1
idea.save()
return keyword_detail(request, keyword_name_slug)
else:
print(form.errors)
context_dict['form'] = form
return render(request, 'openminds/keyword.html', context_dict)
keyword.html
[...]
<h3>Add a new Idea</h3>
<div>
<form id="idea_form" method="post" action="">{% csrf_token %}
{% for hidden in forms.hidden_fields %}
{{ hidden }}
{% endfor %}
{% for field in forms.visible_fields %}
{{ field.errors }}
{{ field }}
{% endfor %}
<input type="submit" name="submit" value="Add Idea" />
</form>
</div>
I think you're passing in form to the template, but attempting to use forms.

How can I add the actual date automatically in Django Forms?

Im new in Django, and Im trying to create a form for add books for my app. But I want the date of publication not included in the form. Instead I want the current system date is obtained and will " add" the form to save my model . How could I do this?
There is part of my views.py:
def add_book(request):
if request.method == 'POST':
form = BookForm(request.POST)
if form.is_valid():
new_book = form.save(commit=False)
new_book.publication_date = django_timezone
new_book.save()
return HttpResponseRedirect('/thanks/')
else:
print form.errors
else:
form = BookForm()
return render_to_response('add_book.html',{'form':form})
There is my forms.py:
class BookForm(ModelForm):
class Meta:
model = Book
exclude = ('publication_date',)
And my model Book:
class Book(models.Model):
title = models.CharField(max_length = 100)
authors = models.ManyToManyField(Author)
publisher = models.ForeignKey(Publisher)
publication_date = models.DateField()
num_pages = models.IntegerField(blank = True, null = True)
class Admin:
list_display = ('title', 'publisher', 'publication_date')
list_filter = ('publisher', 'publication_date')
ordering = ('-publication_date',)
search_fields = ('title',)
def __str__(self):
return self.title
I used this template for form:
{% extends 'base.html' %}
{% block title %}Add a new Book{% endblock %}
{% block content %}
<h3> Here you can add a new book into the local DataBase </h3>
<form action="." method="post">{% csrf_token %}>
<div class="fieldWrapper">
{{ form.title.errors }}
<label for="id_title">Book Title</label>
{{ form.title }}
</div>
<div class="fieldWrapper">
{{ form.authors.errors }}
<label for="id_authors">Authores</label>
{{ form.authors }}
</div>
<div class="fieldWrapper">
{{ form.publisher.errors }}
<label for="id_publisher">Publishers</label>
{{ form.publisher }}
</div>
<div class="fieldWrapper">
{{ form.num_pages.errors }}
<label for="id_num_pages">Number of Pages</label>
{{ form.num_pages }}
</div>
<p><input type="submit" value="Submit"></p>
</form>
{% endblock %}
I've temporarily disabled Django csrf because I do not need for my purpose
To do that, you need to pass the default argument as timezone.now in publication_date model field.
models.py
class Book(models.Model):
title = models.CharField(max_length = 100)
authors = models.ManyToManyField(Author)
publisher = models.ForeignKey(Publisher)
# pass the default argument in publication_date field
publication_date = models.DateField(default=timezone.now)
num_pages = models.IntegerField(blank = True, null = True)
class Admin:
list_display = ('title', 'publisher', 'publication_date')
list_filter = ('publisher', 'publication_date')
ordering = ('-publication_date',)
search_fields = ('title',)
def __str__(self):
return self.title
views.py
After doing that, you can directly call .save() on modelform. Now, the book object will be created with the aware datetime.
def add_book(request):
if request.method == 'POST':
form = BookForm(request.POST)
if form.is_valid():
new_book = form.save() # directly save the book object
return HttpResponseRedirect('/thanks/')
else:
print form.errors
else:
form = BookForm()
return render_to_response('add_book.html',{'form':form})
Why we did not use auto_now_add argument in the model field?
From django's auto_now_add documentation:
Django's auto_now_add uses current time which is not timezone aware. So, it is better to explicitly specify default value using default argument.
If you want to be able to modify this field, set default=timezone.now
(from django.utils.timezone.now()) instead of auto_now_add=True.

Categories